Skip to content

Commit

Permalink
[release/7.0-rc1] Fix nullable annotations on generic math interfaces (
Browse files Browse the repository at this point in the history
…#74116)

* Fix nullable annotations on generic math interfaces

- All `where TSelf : ...` constraints become `where TSelf : ...?`.  Without this, trying to define a type like `Matrix<T> where T : INumber<T>?` in order to support nullable T types warns because `INumber<T>` constrains its `T` (`TSelf`) to be non-nullable.
- All `where TOther : ...` constraints are changed to be oblivious. They can't be correctly annotated as there's no way to express the nullability relationship with the nullability of TSelf.
- Use `[MaybeNullWhen(false)] out T` instead of `[NotNullWhen(true)] out T?`, as we do with other generics, since if the instantiation of `T` is nullable, we can't guarantee `NotNullWhen(true)`.
- Make `IEqualityOperators` `==` and `!=` accept `TSelf?`. This keeps it consistent with `IEquatable<T>.Equals(T?)`, `IEqualityComparer<in T>.Equals(T?, T?)`, `IEqualityComparer.Equals(object?, object?)`, `IStructuralEquatable.Equals(object?, IEqualityComparer)`, and `object.Equals(object?)` which all allow null even if generic and the generic is non-null. It in turn enables checks like `T.Zero == default` without nullability warnings.

* Address PR feedback

Co-authored-by: Stephen Toub <stoub@microsoft.com>
  • Loading branch information
github-actions[bot] and stephentoub committed Aug 18, 2022
1 parent 7ab10ff commit 68aebc1
Show file tree
Hide file tree
Showing 60 changed files with 340 additions and 280 deletions.
12 changes: 6 additions & 6 deletions src/libraries/System.Private.CoreLib/src/System/Byte.cs
Original file line number Diff line number Diff line change
Expand Up @@ -920,7 +920,7 @@ private static bool TryConvertFromTruncating<TOther>(TOther value, out byte resu

/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToChecked{TOther}(TSelf, out TOther)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static bool INumberBase<byte>.TryConvertToChecked<TOther>(byte value, [NotNullWhen(true)] out TOther result)
static bool INumberBase<byte>.TryConvertToChecked<TOther>(byte value, [MaybeNullWhen(false)] out TOther result)
{
// In order to reduce overall code duplication and improve the inlinabilty of these
// methods for the corelib types we have `ConvertFrom` handle the same sign and
Expand Down Expand Up @@ -987,14 +987,14 @@ static bool INumberBase<byte>.TryConvertToChecked<TOther>(byte value, [NotNullWh
}
else
{
result = default!;
result = default;
return false;
}
}

/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToSaturating{TOther}(TSelf, out TOther)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static bool INumberBase<byte>.TryConvertToSaturating<TOther>(byte value, [NotNullWhen(true)] out TOther result)
static bool INumberBase<byte>.TryConvertToSaturating<TOther>(byte value, [MaybeNullWhen(false)] out TOther result)
{
// In order to reduce overall code duplication and improve the inlinabilty of these
// methods for the corelib types we have `ConvertFrom` handle the same sign and
Expand Down Expand Up @@ -1061,14 +1061,14 @@ static bool INumberBase<byte>.TryConvertToSaturating<TOther>(byte value, [NotNul
}
else
{
result = default!;
result = default;
return false;
}
}

/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToTruncating{TOther}(TSelf, out TOther)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static bool INumberBase<byte>.TryConvertToTruncating<TOther>(byte value, [NotNullWhen(true)] out TOther result)
static bool INumberBase<byte>.TryConvertToTruncating<TOther>(byte value, [MaybeNullWhen(false)] out TOther result)
{
// In order to reduce overall code duplication and improve the inlinabilty of these
// methods for the corelib types we have `ConvertFrom` handle the same sign and
Expand Down Expand Up @@ -1135,7 +1135,7 @@ static bool INumberBase<byte>.TryConvertToTruncating<TOther>(byte value, [NotNul
}
else
{
result = default!;
result = default;
return false;
}
}
Expand Down
12 changes: 6 additions & 6 deletions src/libraries/System.Private.CoreLib/src/System/Char.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1715,7 +1715,7 @@ static bool INumberBase<char>.TryConvertFromTruncating<TOther>(TOther value, out

/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToChecked{TOther}(TSelf, out TOther)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static bool INumberBase<char>.TryConvertToChecked<TOther>(char value, [NotNullWhen(true)] out TOther result)
static bool INumberBase<char>.TryConvertToChecked<TOther>(char value, [MaybeNullWhen(false)] out TOther result)
{
// In order to reduce overall code duplication and improve the inlinabilty of these
// methods for the corelib types we have `ConvertFrom` handle the same sign and
Expand Down Expand Up @@ -1782,14 +1782,14 @@ static bool INumberBase<char>.TryConvertToChecked<TOther>(char value, [NotNullWh
}
else
{
result = default!;
result = default;
return false;
}
}

/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToSaturating{TOther}(TSelf, out TOther)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static bool INumberBase<char>.TryConvertToSaturating<TOther>(char value, [NotNullWhen(true)] out TOther result)
static bool INumberBase<char>.TryConvertToSaturating<TOther>(char value, [MaybeNullWhen(false)] out TOther result)
{
// In order to reduce overall code duplication and improve the inlinabilty of these
// methods for the corelib types we have `ConvertFrom` handle the same sign and
Expand Down Expand Up @@ -1856,14 +1856,14 @@ static bool INumberBase<char>.TryConvertToSaturating<TOther>(char value, [NotNul
}
else
{
result = default!;
result = default;
return false;
}
}

/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToTruncating{TOther}(TSelf, out TOther)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static bool INumberBase<char>.TryConvertToTruncating<TOther>(char value, [NotNullWhen(true)] out TOther result)
static bool INumberBase<char>.TryConvertToTruncating<TOther>(char value, [MaybeNullWhen(false)] out TOther result)
{
// In order to reduce overall code duplication and improve the inlinabilty of these
// methods for the corelib types we have `ConvertFrom` handle the same sign and
Expand Down Expand Up @@ -1930,7 +1930,7 @@ static bool INumberBase<char>.TryConvertToTruncating<TOther>(char value, [NotNul
}
else
{
result = default!;
result = default;
return false;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -683,7 +683,7 @@ public bool TryDequeue([MaybeNullWhen(false)] out T result)
// check and this check, another item could have arrived).
if (head._nextSegment == null)
{
result = default!;
result = default;
return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ public bool TryDequeue([MaybeNullWhen(false)] out T result)

if (_size == 0)
{
result = default!;
result = default;
return false;
}

Expand Down Expand Up @@ -263,7 +263,7 @@ public bool TryPeek([MaybeNullWhen(false)] out T result)
{
if (_size == 0)
{
result = default!;
result = default;
return false;
}

Expand Down
12 changes: 6 additions & 6 deletions src/libraries/System.Private.CoreLib/src/System/Decimal.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1645,7 +1645,7 @@ private static bool TryConvertFrom<TOther>(TOther value, out decimal result)

/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToChecked{TOther}(TSelf, out TOther)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static bool INumberBase<decimal>.TryConvertToChecked<TOther>(decimal value, [NotNullWhen(true)] out TOther result)
static bool INumberBase<decimal>.TryConvertToChecked<TOther>(decimal value, [MaybeNullWhen(false)] out TOther result)
{
// In order to reduce overall code duplication and improve the inlinabilty of these
// methods for the corelib types we have `ConvertFrom` handle the same sign and
Expand Down Expand Up @@ -1712,26 +1712,26 @@ static bool INumberBase<decimal>.TryConvertToChecked<TOther>(decimal value, [Not
}
else
{
result = default!;
result = default;
return false;
}
}

/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToSaturating{TOther}(TSelf, out TOther)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static bool INumberBase<decimal>.TryConvertToSaturating<TOther>(decimal value, [NotNullWhen(true)] out TOther result)
static bool INumberBase<decimal>.TryConvertToSaturating<TOther>(decimal value, [MaybeNullWhen(false)] out TOther result)
{
return TryConvertTo<TOther>(value, out result);
}

/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToTruncating{TOther}(TSelf, out TOther)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static bool INumberBase<decimal>.TryConvertToTruncating<TOther>(decimal value, [NotNullWhen(true)] out TOther result)
static bool INumberBase<decimal>.TryConvertToTruncating<TOther>(decimal value, [MaybeNullWhen(false)] out TOther result)
{
return TryConvertTo<TOther>(value, out result);
}

private static bool TryConvertTo<TOther>(decimal value, [NotNullWhen(true)] out TOther result)
private static bool TryConvertTo<TOther>(decimal value, [MaybeNullWhen(false)] out TOther result)
where TOther : INumberBase<TOther>
{
// In order to reduce overall code duplication and improve the inlinabilty of these
Expand Down Expand Up @@ -1804,7 +1804,7 @@ private static bool TryConvertTo<TOther>(decimal value, [NotNullWhen(true)] out
}
else
{
result = default!;
result = default;
return false;
}
}
Expand Down
12 changes: 6 additions & 6 deletions src/libraries/System.Private.CoreLib/src/System/Double.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1243,7 +1243,7 @@ private static bool TryConvertFrom<TOther>(TOther value, out double result)

/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToChecked{TOther}(TSelf, out TOther)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static bool INumberBase<double>.TryConvertToChecked<TOther>(double value, [NotNullWhen(true)] out TOther result)
static bool INumberBase<double>.TryConvertToChecked<TOther>(double value, [MaybeNullWhen(false)] out TOther result)
{
// In order to reduce overall code duplication and improve the inlinabilty of these
// methods for the corelib types we have `ConvertFrom` handle the same sign and
Expand Down Expand Up @@ -1304,26 +1304,26 @@ static bool INumberBase<double>.TryConvertToChecked<TOther>(double value, [NotNu
}
else
{
result = default!;
result = default;
return false;
}
}

/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToSaturating{TOther}(TSelf, out TOther)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static bool INumberBase<double>.TryConvertToSaturating<TOther>(double value, [NotNullWhen(true)] out TOther result)
static bool INumberBase<double>.TryConvertToSaturating<TOther>(double value, [MaybeNullWhen(false)] out TOther result)
{
return TryConvertTo<TOther>(value, out result);
}

/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToTruncating{TOther}(TSelf, out TOther)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static bool INumberBase<double>.TryConvertToTruncating<TOther>(double value, [NotNullWhen(true)] out TOther result)
static bool INumberBase<double>.TryConvertToTruncating<TOther>(double value, [MaybeNullWhen(false)] out TOther result)
{
return TryConvertTo<TOther>(value, out result);
}

private static bool TryConvertTo<TOther>(double value, [NotNullWhen(true)] out TOther result)
private static bool TryConvertTo<TOther>(double value, [MaybeNullWhen(false)] out TOther result)
where TOther : INumberBase<TOther>
{
// In order to reduce overall code duplication and improve the inlinabilty of these
Expand Down Expand Up @@ -1402,7 +1402,7 @@ private static bool TryConvertTo<TOther>(double value, [NotNullWhen(true)] out T
}
else
{
result = default!;
result = default;
return false;
}
}
Expand Down
12 changes: 6 additions & 6 deletions src/libraries/System.Private.CoreLib/src/System/Half.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1727,7 +1727,7 @@ private static bool TryConvertFrom<TOther>(TOther value, out Half result)

/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToChecked{TOther}(TSelf, out TOther)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static bool INumberBase<Half>.TryConvertToChecked<TOther>(Half value, [NotNullWhen(true)] out TOther result)
static bool INumberBase<Half>.TryConvertToChecked<TOther>(Half value, [MaybeNullWhen(false)] out TOther result)
{
// In order to reduce overall code duplication and improve the inlinabilty of these
// methods for the corelib types we have `ConvertFrom` handle the same sign and
Expand Down Expand Up @@ -1788,26 +1788,26 @@ static bool INumberBase<Half>.TryConvertToChecked<TOther>(Half value, [NotNullWh
}
else
{
result = default!;
result = default;
return false;
}
}

/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToSaturating{TOther}(TSelf, out TOther)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static bool INumberBase<Half>.TryConvertToSaturating<TOther>(Half value, [NotNullWhen(true)] out TOther result)
static bool INumberBase<Half>.TryConvertToSaturating<TOther>(Half value, [MaybeNullWhen(false)] out TOther result)
{
return TryConvertTo<TOther>(value, out result);
}

/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToTruncating{TOther}(TSelf, out TOther)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static bool INumberBase<Half>.TryConvertToTruncating<TOther>(Half value, [NotNullWhen(true)] out TOther result)
static bool INumberBase<Half>.TryConvertToTruncating<TOther>(Half value, [MaybeNullWhen(false)] out TOther result)
{
return TryConvertTo<TOther>(value, out result);
}

private static bool TryConvertTo<TOther>(Half value, [NotNullWhen(true)] out TOther result)
private static bool TryConvertTo<TOther>(Half value, [MaybeNullWhen(false)] out TOther result)
where TOther : INumberBase<TOther>
{
// In order to reduce overall code duplication and improve the inlinabilty of these
Expand Down Expand Up @@ -1879,7 +1879,7 @@ private static bool TryConvertTo<TOther>(Half value, [NotNullWhen(true)] out TOt
}
else
{
result = default!;
result = default;
return false;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace System
/// <summary>Defines a mechanism for parsing a string to a value.</summary>
/// <typeparam name="TSelf">The type that implements this interface.</typeparam>
public interface IParsable<TSelf>
where TSelf : IParsable<TSelf>
where TSelf : IParsable<TSelf>?
{
/// <summary>Parses a string into a value.</summary>
/// <param name="s">The string to parse.</param>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace System
/// <summary>Defines a mechanism for parsing a span of characters to a value.</summary>
/// <typeparam name="TSelf">The type that implements this interface.</typeparam>
public interface ISpanParsable<TSelf> : IParsable<TSelf>
where TSelf : ISpanParsable<TSelf>
where TSelf : ISpanParsable<TSelf>?
{
/// <summary>Parses a span of characters into a value.</summary>
/// <param name="s">The span of characters to parse.</param>
Expand Down
12 changes: 6 additions & 6 deletions src/libraries/System.Private.CoreLib/src/System/Int128.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1865,7 +1865,7 @@ private static bool TryConvertFromTruncating<TOther>(TOther value, out Int128 re

/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToChecked{TOther}(TSelf, out TOther)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static bool INumberBase<Int128>.TryConvertToChecked<TOther>(Int128 value, [NotNullWhen(true)] out TOther result)
static bool INumberBase<Int128>.TryConvertToChecked<TOther>(Int128 value, [MaybeNullWhen(false)] out TOther result)
{
// In order to reduce overall code duplication and improve the inlinabilty of these
// methods for the corelib types we have `ConvertFrom` handle the same sign and
Expand Down Expand Up @@ -1926,14 +1926,14 @@ static bool INumberBase<Int128>.TryConvertToChecked<TOther>(Int128 value, [NotNu
}
else
{
result = default!;
result = default;
return false;
}
}

/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToSaturating{TOther}(TSelf, out TOther)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static bool INumberBase<Int128>.TryConvertToSaturating<TOther>(Int128 value, [NotNullWhen(true)] out TOther result)
static bool INumberBase<Int128>.TryConvertToSaturating<TOther>(Int128 value, [MaybeNullWhen(false)] out TOther result)
{
// In order to reduce overall code duplication and improve the inlinabilty of these
// methods for the corelib types we have `ConvertFrom` handle the same sign and
Expand Down Expand Up @@ -2000,14 +2000,14 @@ static bool INumberBase<Int128>.TryConvertToSaturating<TOther>(Int128 value, [No
}
else
{
result = default!;
result = default;
return false;
}
}

/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToTruncating{TOther}(TSelf, out TOther)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static bool INumberBase<Int128>.TryConvertToTruncating<TOther>(Int128 value, [NotNullWhen(true)] out TOther result)
static bool INumberBase<Int128>.TryConvertToTruncating<TOther>(Int128 value, [MaybeNullWhen(false)] out TOther result)
{
// In order to reduce overall code duplication and improve the inlinabilty of these
// methods for the corelib types we have `ConvertFrom` handle the same sign and
Expand Down Expand Up @@ -2069,7 +2069,7 @@ static bool INumberBase<Int128>.TryConvertToTruncating<TOther>(Int128 value, [No
}
else
{
result = default!;
result = default;
return false;
}
}
Expand Down
Loading

0 comments on commit 68aebc1

Please sign in to comment.