Skip to content

Commit 68aebc1

Browse files
[release/7.0-rc1] Fix nullable annotations on generic math interfaces (#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>
1 parent 7ab10ff commit 68aebc1

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+340
-280
lines changed

src/libraries/System.Private.CoreLib/src/System/Byte.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -920,7 +920,7 @@ private static bool TryConvertFromTruncating<TOther>(TOther value, out byte resu
920920

921921
/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToChecked{TOther}(TSelf, out TOther)" />
922922
[MethodImpl(MethodImplOptions.AggressiveInlining)]
923-
static bool INumberBase<byte>.TryConvertToChecked<TOther>(byte value, [NotNullWhen(true)] out TOther result)
923+
static bool INumberBase<byte>.TryConvertToChecked<TOther>(byte value, [MaybeNullWhen(false)] out TOther result)
924924
{
925925
// In order to reduce overall code duplication and improve the inlinabilty of these
926926
// methods for the corelib types we have `ConvertFrom` handle the same sign and
@@ -987,14 +987,14 @@ static bool INumberBase<byte>.TryConvertToChecked<TOther>(byte value, [NotNullWh
987987
}
988988
else
989989
{
990-
result = default!;
990+
result = default;
991991
return false;
992992
}
993993
}
994994

995995
/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToSaturating{TOther}(TSelf, out TOther)" />
996996
[MethodImpl(MethodImplOptions.AggressiveInlining)]
997-
static bool INumberBase<byte>.TryConvertToSaturating<TOther>(byte value, [NotNullWhen(true)] out TOther result)
997+
static bool INumberBase<byte>.TryConvertToSaturating<TOther>(byte value, [MaybeNullWhen(false)] out TOther result)
998998
{
999999
// In order to reduce overall code duplication and improve the inlinabilty of these
10001000
// methods for the corelib types we have `ConvertFrom` handle the same sign and
@@ -1061,14 +1061,14 @@ static bool INumberBase<byte>.TryConvertToSaturating<TOther>(byte value, [NotNul
10611061
}
10621062
else
10631063
{
1064-
result = default!;
1064+
result = default;
10651065
return false;
10661066
}
10671067
}
10681068

10691069
/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToTruncating{TOther}(TSelf, out TOther)" />
10701070
[MethodImpl(MethodImplOptions.AggressiveInlining)]
1071-
static bool INumberBase<byte>.TryConvertToTruncating<TOther>(byte value, [NotNullWhen(true)] out TOther result)
1071+
static bool INumberBase<byte>.TryConvertToTruncating<TOther>(byte value, [MaybeNullWhen(false)] out TOther result)
10721072
{
10731073
// In order to reduce overall code duplication and improve the inlinabilty of these
10741074
// methods for the corelib types we have `ConvertFrom` handle the same sign and
@@ -1135,7 +1135,7 @@ static bool INumberBase<byte>.TryConvertToTruncating<TOther>(byte value, [NotNul
11351135
}
11361136
else
11371137
{
1138-
result = default!;
1138+
result = default;
11391139
return false;
11401140
}
11411141
}

src/libraries/System.Private.CoreLib/src/System/Char.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1715,7 +1715,7 @@ static bool INumberBase<char>.TryConvertFromTruncating<TOther>(TOther value, out
17151715

17161716
/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToChecked{TOther}(TSelf, out TOther)" />
17171717
[MethodImpl(MethodImplOptions.AggressiveInlining)]
1718-
static bool INumberBase<char>.TryConvertToChecked<TOther>(char value, [NotNullWhen(true)] out TOther result)
1718+
static bool INumberBase<char>.TryConvertToChecked<TOther>(char value, [MaybeNullWhen(false)] out TOther result)
17191719
{
17201720
// In order to reduce overall code duplication and improve the inlinabilty of these
17211721
// methods for the corelib types we have `ConvertFrom` handle the same sign and
@@ -1782,14 +1782,14 @@ static bool INumberBase<char>.TryConvertToChecked<TOther>(char value, [NotNullWh
17821782
}
17831783
else
17841784
{
1785-
result = default!;
1785+
result = default;
17861786
return false;
17871787
}
17881788
}
17891789

17901790
/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToSaturating{TOther}(TSelf, out TOther)" />
17911791
[MethodImpl(MethodImplOptions.AggressiveInlining)]
1792-
static bool INumberBase<char>.TryConvertToSaturating<TOther>(char value, [NotNullWhen(true)] out TOther result)
1792+
static bool INumberBase<char>.TryConvertToSaturating<TOther>(char value, [MaybeNullWhen(false)] out TOther result)
17931793
{
17941794
// In order to reduce overall code duplication and improve the inlinabilty of these
17951795
// methods for the corelib types we have `ConvertFrom` handle the same sign and
@@ -1856,14 +1856,14 @@ static bool INumberBase<char>.TryConvertToSaturating<TOther>(char value, [NotNul
18561856
}
18571857
else
18581858
{
1859-
result = default!;
1859+
result = default;
18601860
return false;
18611861
}
18621862
}
18631863

18641864
/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToTruncating{TOther}(TSelf, out TOther)" />
18651865
[MethodImpl(MethodImplOptions.AggressiveInlining)]
1866-
static bool INumberBase<char>.TryConvertToTruncating<TOther>(char value, [NotNullWhen(true)] out TOther result)
1866+
static bool INumberBase<char>.TryConvertToTruncating<TOther>(char value, [MaybeNullWhen(false)] out TOther result)
18671867
{
18681868
// In order to reduce overall code duplication and improve the inlinabilty of these
18691869
// methods for the corelib types we have `ConvertFrom` handle the same sign and
@@ -1930,7 +1930,7 @@ static bool INumberBase<char>.TryConvertToTruncating<TOther>(char value, [NotNul
19301930
}
19311931
else
19321932
{
1933-
result = default!;
1933+
result = default;
19341934
return false;
19351935
}
19361936
}

src/libraries/System.Private.CoreLib/src/System/Collections/Concurrent/ConcurrentQueue.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -683,7 +683,7 @@ public bool TryDequeue([MaybeNullWhen(false)] out T result)
683683
// check and this check, another item could have arrived).
684684
if (head._nextSegment == null)
685685
{
686-
result = default!;
686+
result = default;
687687
return false;
688688
}
689689

src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Queue.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ public bool TryDequeue([MaybeNullWhen(false)] out T result)
231231

232232
if (_size == 0)
233233
{
234-
result = default!;
234+
result = default;
235235
return false;
236236
}
237237

@@ -263,7 +263,7 @@ public bool TryPeek([MaybeNullWhen(false)] out T result)
263263
{
264264
if (_size == 0)
265265
{
266-
result = default!;
266+
result = default;
267267
return false;
268268
}
269269

src/libraries/System.Private.CoreLib/src/System/Decimal.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1645,7 +1645,7 @@ private static bool TryConvertFrom<TOther>(TOther value, out decimal result)
16451645

16461646
/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToChecked{TOther}(TSelf, out TOther)" />
16471647
[MethodImpl(MethodImplOptions.AggressiveInlining)]
1648-
static bool INumberBase<decimal>.TryConvertToChecked<TOther>(decimal value, [NotNullWhen(true)] out TOther result)
1648+
static bool INumberBase<decimal>.TryConvertToChecked<TOther>(decimal value, [MaybeNullWhen(false)] out TOther result)
16491649
{
16501650
// In order to reduce overall code duplication and improve the inlinabilty of these
16511651
// methods for the corelib types we have `ConvertFrom` handle the same sign and
@@ -1712,26 +1712,26 @@ static bool INumberBase<decimal>.TryConvertToChecked<TOther>(decimal value, [Not
17121712
}
17131713
else
17141714
{
1715-
result = default!;
1715+
result = default;
17161716
return false;
17171717
}
17181718
}
17191719

17201720
/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToSaturating{TOther}(TSelf, out TOther)" />
17211721
[MethodImpl(MethodImplOptions.AggressiveInlining)]
1722-
static bool INumberBase<decimal>.TryConvertToSaturating<TOther>(decimal value, [NotNullWhen(true)] out TOther result)
1722+
static bool INumberBase<decimal>.TryConvertToSaturating<TOther>(decimal value, [MaybeNullWhen(false)] out TOther result)
17231723
{
17241724
return TryConvertTo<TOther>(value, out result);
17251725
}
17261726

17271727
/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToTruncating{TOther}(TSelf, out TOther)" />
17281728
[MethodImpl(MethodImplOptions.AggressiveInlining)]
1729-
static bool INumberBase<decimal>.TryConvertToTruncating<TOther>(decimal value, [NotNullWhen(true)] out TOther result)
1729+
static bool INumberBase<decimal>.TryConvertToTruncating<TOther>(decimal value, [MaybeNullWhen(false)] out TOther result)
17301730
{
17311731
return TryConvertTo<TOther>(value, out result);
17321732
}
17331733

1734-
private static bool TryConvertTo<TOther>(decimal value, [NotNullWhen(true)] out TOther result)
1734+
private static bool TryConvertTo<TOther>(decimal value, [MaybeNullWhen(false)] out TOther result)
17351735
where TOther : INumberBase<TOther>
17361736
{
17371737
// In order to reduce overall code duplication and improve the inlinabilty of these
@@ -1804,7 +1804,7 @@ private static bool TryConvertTo<TOther>(decimal value, [NotNullWhen(true)] out
18041804
}
18051805
else
18061806
{
1807-
result = default!;
1807+
result = default;
18081808
return false;
18091809
}
18101810
}

src/libraries/System.Private.CoreLib/src/System/Double.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1243,7 +1243,7 @@ private static bool TryConvertFrom<TOther>(TOther value, out double result)
12431243

12441244
/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToChecked{TOther}(TSelf, out TOther)" />
12451245
[MethodImpl(MethodImplOptions.AggressiveInlining)]
1246-
static bool INumberBase<double>.TryConvertToChecked<TOther>(double value, [NotNullWhen(true)] out TOther result)
1246+
static bool INumberBase<double>.TryConvertToChecked<TOther>(double value, [MaybeNullWhen(false)] out TOther result)
12471247
{
12481248
// In order to reduce overall code duplication and improve the inlinabilty of these
12491249
// methods for the corelib types we have `ConvertFrom` handle the same sign and
@@ -1304,26 +1304,26 @@ static bool INumberBase<double>.TryConvertToChecked<TOther>(double value, [NotNu
13041304
}
13051305
else
13061306
{
1307-
result = default!;
1307+
result = default;
13081308
return false;
13091309
}
13101310
}
13111311

13121312
/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToSaturating{TOther}(TSelf, out TOther)" />
13131313
[MethodImpl(MethodImplOptions.AggressiveInlining)]
1314-
static bool INumberBase<double>.TryConvertToSaturating<TOther>(double value, [NotNullWhen(true)] out TOther result)
1314+
static bool INumberBase<double>.TryConvertToSaturating<TOther>(double value, [MaybeNullWhen(false)] out TOther result)
13151315
{
13161316
return TryConvertTo<TOther>(value, out result);
13171317
}
13181318

13191319
/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToTruncating{TOther}(TSelf, out TOther)" />
13201320
[MethodImpl(MethodImplOptions.AggressiveInlining)]
1321-
static bool INumberBase<double>.TryConvertToTruncating<TOther>(double value, [NotNullWhen(true)] out TOther result)
1321+
static bool INumberBase<double>.TryConvertToTruncating<TOther>(double value, [MaybeNullWhen(false)] out TOther result)
13221322
{
13231323
return TryConvertTo<TOther>(value, out result);
13241324
}
13251325

1326-
private static bool TryConvertTo<TOther>(double value, [NotNullWhen(true)] out TOther result)
1326+
private static bool TryConvertTo<TOther>(double value, [MaybeNullWhen(false)] out TOther result)
13271327
where TOther : INumberBase<TOther>
13281328
{
13291329
// In order to reduce overall code duplication and improve the inlinabilty of these
@@ -1402,7 +1402,7 @@ private static bool TryConvertTo<TOther>(double value, [NotNullWhen(true)] out T
14021402
}
14031403
else
14041404
{
1405-
result = default!;
1405+
result = default;
14061406
return false;
14071407
}
14081408
}

src/libraries/System.Private.CoreLib/src/System/Half.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1727,7 +1727,7 @@ private static bool TryConvertFrom<TOther>(TOther value, out Half result)
17271727

17281728
/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToChecked{TOther}(TSelf, out TOther)" />
17291729
[MethodImpl(MethodImplOptions.AggressiveInlining)]
1730-
static bool INumberBase<Half>.TryConvertToChecked<TOther>(Half value, [NotNullWhen(true)] out TOther result)
1730+
static bool INumberBase<Half>.TryConvertToChecked<TOther>(Half value, [MaybeNullWhen(false)] out TOther result)
17311731
{
17321732
// In order to reduce overall code duplication and improve the inlinabilty of these
17331733
// methods for the corelib types we have `ConvertFrom` handle the same sign and
@@ -1788,26 +1788,26 @@ static bool INumberBase<Half>.TryConvertToChecked<TOther>(Half value, [NotNullWh
17881788
}
17891789
else
17901790
{
1791-
result = default!;
1791+
result = default;
17921792
return false;
17931793
}
17941794
}
17951795

17961796
/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToSaturating{TOther}(TSelf, out TOther)" />
17971797
[MethodImpl(MethodImplOptions.AggressiveInlining)]
1798-
static bool INumberBase<Half>.TryConvertToSaturating<TOther>(Half value, [NotNullWhen(true)] out TOther result)
1798+
static bool INumberBase<Half>.TryConvertToSaturating<TOther>(Half value, [MaybeNullWhen(false)] out TOther result)
17991799
{
18001800
return TryConvertTo<TOther>(value, out result);
18011801
}
18021802

18031803
/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToTruncating{TOther}(TSelf, out TOther)" />
18041804
[MethodImpl(MethodImplOptions.AggressiveInlining)]
1805-
static bool INumberBase<Half>.TryConvertToTruncating<TOther>(Half value, [NotNullWhen(true)] out TOther result)
1805+
static bool INumberBase<Half>.TryConvertToTruncating<TOther>(Half value, [MaybeNullWhen(false)] out TOther result)
18061806
{
18071807
return TryConvertTo<TOther>(value, out result);
18081808
}
18091809

1810-
private static bool TryConvertTo<TOther>(Half value, [NotNullWhen(true)] out TOther result)
1810+
private static bool TryConvertTo<TOther>(Half value, [MaybeNullWhen(false)] out TOther result)
18111811
where TOther : INumberBase<TOther>
18121812
{
18131813
// In order to reduce overall code duplication and improve the inlinabilty of these
@@ -1879,7 +1879,7 @@ private static bool TryConvertTo<TOther>(Half value, [NotNullWhen(true)] out TOt
18791879
}
18801880
else
18811881
{
1882-
result = default!;
1882+
result = default;
18831883
return false;
18841884
}
18851885
}

src/libraries/System.Private.CoreLib/src/System/IParsable.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace System
88
/// <summary>Defines a mechanism for parsing a string to a value.</summary>
99
/// <typeparam name="TSelf">The type that implements this interface.</typeparam>
1010
public interface IParsable<TSelf>
11-
where TSelf : IParsable<TSelf>
11+
where TSelf : IParsable<TSelf>?
1212
{
1313
/// <summary>Parses a string into a value.</summary>
1414
/// <param name="s">The string to parse.</param>

src/libraries/System.Private.CoreLib/src/System/ISpanParsable.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace System
88
/// <summary>Defines a mechanism for parsing a span of characters to a value.</summary>
99
/// <typeparam name="TSelf">The type that implements this interface.</typeparam>
1010
public interface ISpanParsable<TSelf> : IParsable<TSelf>
11-
where TSelf : ISpanParsable<TSelf>
11+
where TSelf : ISpanParsable<TSelf>?
1212
{
1313
/// <summary>Parses a span of characters into a value.</summary>
1414
/// <param name="s">The span of characters to parse.</param>

src/libraries/System.Private.CoreLib/src/System/Int128.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1865,7 +1865,7 @@ private static bool TryConvertFromTruncating<TOther>(TOther value, out Int128 re
18651865

18661866
/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToChecked{TOther}(TSelf, out TOther)" />
18671867
[MethodImpl(MethodImplOptions.AggressiveInlining)]
1868-
static bool INumberBase<Int128>.TryConvertToChecked<TOther>(Int128 value, [NotNullWhen(true)] out TOther result)
1868+
static bool INumberBase<Int128>.TryConvertToChecked<TOther>(Int128 value, [MaybeNullWhen(false)] out TOther result)
18691869
{
18701870
// In order to reduce overall code duplication and improve the inlinabilty of these
18711871
// methods for the corelib types we have `ConvertFrom` handle the same sign and
@@ -1926,14 +1926,14 @@ static bool INumberBase<Int128>.TryConvertToChecked<TOther>(Int128 value, [NotNu
19261926
}
19271927
else
19281928
{
1929-
result = default!;
1929+
result = default;
19301930
return false;
19311931
}
19321932
}
19331933

19341934
/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToSaturating{TOther}(TSelf, out TOther)" />
19351935
[MethodImpl(MethodImplOptions.AggressiveInlining)]
1936-
static bool INumberBase<Int128>.TryConvertToSaturating<TOther>(Int128 value, [NotNullWhen(true)] out TOther result)
1936+
static bool INumberBase<Int128>.TryConvertToSaturating<TOther>(Int128 value, [MaybeNullWhen(false)] out TOther result)
19371937
{
19381938
// In order to reduce overall code duplication and improve the inlinabilty of these
19391939
// methods for the corelib types we have `ConvertFrom` handle the same sign and
@@ -2000,14 +2000,14 @@ static bool INumberBase<Int128>.TryConvertToSaturating<TOther>(Int128 value, [No
20002000
}
20012001
else
20022002
{
2003-
result = default!;
2003+
result = default;
20042004
return false;
20052005
}
20062006
}
20072007

20082008
/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToTruncating{TOther}(TSelf, out TOther)" />
20092009
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2010-
static bool INumberBase<Int128>.TryConvertToTruncating<TOther>(Int128 value, [NotNullWhen(true)] out TOther result)
2010+
static bool INumberBase<Int128>.TryConvertToTruncating<TOther>(Int128 value, [MaybeNullWhen(false)] out TOther result)
20112011
{
20122012
// In order to reduce overall code duplication and improve the inlinabilty of these
20132013
// methods for the corelib types we have `ConvertFrom` handle the same sign and
@@ -2069,7 +2069,7 @@ static bool INumberBase<Int128>.TryConvertToTruncating<TOther>(Int128 value, [No
20692069
}
20702070
else
20712071
{
2072-
result = default!;
2072+
result = default;
20732073
return false;
20742074
}
20752075
}

0 commit comments

Comments
 (0)