From e8f46ddfd6cf3dc520574e37fa7168abc9fc1445 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 28 Jul 2020 15:01:53 +0200 Subject: [PATCH 1/7] Add Unsafe.IsNullRef(ref T) --- .../System.Runtime.CompilerServices.Unsafe.cs | 1 + .../System.Runtime.CompilerServices.Unsafe.il | 11 +++ ...System.Runtime.CompilerServices.Unsafe.xml | 7 ++ .../tests/UnsafeTests.cs | 92 +++++++++++++++++++ 4 files changed, 111 insertions(+) diff --git a/src/libraries/System.Runtime.CompilerServices.Unsafe/ref/System.Runtime.CompilerServices.Unsafe.cs b/src/libraries/System.Runtime.CompilerServices.Unsafe/ref/System.Runtime.CompilerServices.Unsafe.cs index ae977a0cdc331..bce4072ff8f43 100644 --- a/src/libraries/System.Runtime.CompilerServices.Unsafe/ref/System.Runtime.CompilerServices.Unsafe.cs +++ b/src/libraries/System.Runtime.CompilerServices.Unsafe/ref/System.Runtime.CompilerServices.Unsafe.cs @@ -35,6 +35,7 @@ public static void InitBlockUnaligned(ref byte startAddress, byte value, uint by public static unsafe void InitBlockUnaligned(void* startAddress, byte value, uint byteCount) { } public static bool IsAddressGreaterThan(ref T left, ref T right) { throw null; } public static bool IsAddressLessThan(ref T left, ref T right) { throw null; } + public static bool IsNullRef(ref T source) { throw null; } public static T ReadUnaligned(ref byte source) { throw null; } public static unsafe T ReadUnaligned(void* source) { throw null; } public static unsafe T Read(void* source) { throw null; } diff --git a/src/libraries/System.Runtime.CompilerServices.Unsafe/src/System.Runtime.CompilerServices.Unsafe.il b/src/libraries/System.Runtime.CompilerServices.Unsafe/src/System.Runtime.CompilerServices.Unsafe.il index 8740fafec41a1..79736eb332e99 100644 --- a/src/libraries/System.Runtime.CompilerServices.Unsafe/src/System.Runtime.CompilerServices.Unsafe.il +++ b/src/libraries/System.Runtime.CompilerServices.Unsafe/src/System.Runtime.CompilerServices.Unsafe.il @@ -458,6 +458,17 @@ ret } // end of method Unsafe::IsAddressLessThan + .method public hidebysig static bool IsNullRef(!!T& source) cil managed aggressiveinlining + { + .custom instance void System.Runtime.Versioning.NonVersionableAttribute::.ctor() = ( 01 00 00 00 ) + .maxstack 2 + ldarg.0 + ldc.i4.0 + conv.u + ceq + ret + } // end of method Unsafe::IsNullRef + } // end of class System.Runtime.CompilerServices.Unsafe .class private auto ansi sealed beforefieldinit System.Runtime.Versioning.NonVersionableAttribute diff --git a/src/libraries/System.Runtime.CompilerServices.Unsafe/src/System.Runtime.CompilerServices.Unsafe.xml b/src/libraries/System.Runtime.CompilerServices.Unsafe/src/System.Runtime.CompilerServices.Unsafe.xml index 1aef90cb9d2ed..597212b2f8cab 100644 --- a/src/libraries/System.Runtime.CompilerServices.Unsafe/src/System.Runtime.CompilerServices.Unsafe.xml +++ b/src/libraries/System.Runtime.CompilerServices.Unsafe/src/System.Runtime.CompilerServices.Unsafe.xml @@ -233,6 +233,13 @@ or the objects being referenced must both be pinned; or both references must represent unmanaged pointers; otherwise the result is undefined. + + + Returns if a given by-ref to type is a null reference. + + The reference to check. + This check is conceptually similar to "(void*)(&source) == nullptr". + Copies bytes from the source address to the destination address. diff --git a/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs b/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs index 88473528f1088..bf9e54060ef52 100644 --- a/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs +++ b/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs @@ -931,6 +931,98 @@ public static void SkipInit_PreservesPrevious() Unsafe.SkipInit(out stringValue); Assert.Equal("25", stringValue); } + + [Fact] + public static void IsNullRef_NotNull() + { + // Validate that calling with primitive types works. + sbyte sbyteValue = 1; + Assert.False(Unsafe.IsNullRef(ref sbyteValue)); + + byte byteValue = 2; + Assert.False(Unsafe.IsNullRef(ref byteValue)); + + short shortValue = 3; + Assert.False(Unsafe.IsNullRef(ref shortValue)); + + ushort ushortValue = 4; + Assert.False(Unsafe.IsNullRef(ref ushortValue)); + + int intValue = 5; + Assert.False(Unsafe.IsNullRef(ref intValue)); + + uint uintValue = 6; + Assert.False(Unsafe.IsNullRef(ref uintValue)); + + long longValue = 7; + Assert.False(Unsafe.IsNullRef(ref longValue)); + + ulong ulongValue = 8; + Assert.False(Unsafe.IsNullRef(ref ulongValue)); + + float floatValue = 9; + Assert.False(Unsafe.IsNullRef(ref floatValue)); + + double doubleValue = 10; + Assert.False(Unsafe.IsNullRef(ref doubleValue)); + + // Validate that calling on user-defined unmanaged structs works. + Byte4 byte4Value = default; + Assert.False(Unsafe.IsNullRef(ref byte4Value)); + + Byte4Short2 byte4Short2Value = default; + Assert.False(Unsafe.IsNullRef(ref byte4Short2Value)); + + Byte512 byte512Value = default; + Assert.False(Unsafe.IsNullRef(ref byte512Value)); + + Int32Double int32DoubleValue = default; + Assert.False(Unsafe.IsNullRef(ref int32DoubleValue)); + + // Validate that calling on reference types works. + object objectValue = new object(); + Assert.False(Unsafe.IsNullRef(ref objectValue)); + + string stringValue = nameof(IsNullRef); + Assert.False(Unsafe.IsNullRef(ref stringValue)); + } + + [Fact] + public static unsafe void IsNullRef_Null() + { + // Validate that calling with primitive types works. + Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); + + Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); + + Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); + + Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); + + Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); + + Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); + + Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); + + Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); + + Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); + + // Validate that calling on user-defined unmanaged structs works. + Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); + + Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); + + Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); + + Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); + + // Validate that calling on reference types works. + Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); + + Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); + } } [StructLayout(LayoutKind.Explicit)] From aea5ef5af3f955b3bda4b56f2de1165acdffae21 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 28 Jul 2020 15:33:03 +0200 Subject: [PATCH 2/7] Add Unsafe.NullRef() --- .../System.Runtime.CompilerServices.Unsafe.cs | 1 + .../System.Runtime.CompilerServices.Unsafe.il | 9 +++++ ...System.Runtime.CompilerServices.Unsafe.xml | 5 +++ .../tests/UnsafeTests.cs | 39 +++++++++++++++++++ 4 files changed, 54 insertions(+) diff --git a/src/libraries/System.Runtime.CompilerServices.Unsafe/ref/System.Runtime.CompilerServices.Unsafe.cs b/src/libraries/System.Runtime.CompilerServices.Unsafe/ref/System.Runtime.CompilerServices.Unsafe.cs index bce4072ff8f43..99c28c3a28b06 100644 --- a/src/libraries/System.Runtime.CompilerServices.Unsafe/ref/System.Runtime.CompilerServices.Unsafe.cs +++ b/src/libraries/System.Runtime.CompilerServices.Unsafe/ref/System.Runtime.CompilerServices.Unsafe.cs @@ -36,6 +36,7 @@ public static unsafe void InitBlockUnaligned(void* startAddress, byte value, uin public static bool IsAddressGreaterThan(ref T left, ref T right) { throw null; } public static bool IsAddressLessThan(ref T left, ref T right) { throw null; } public static bool IsNullRef(ref T source) { throw null; } + public static ref T NullRef() { throw null; } public static T ReadUnaligned(ref byte source) { throw null; } public static unsafe T ReadUnaligned(void* source) { throw null; } public static unsafe T Read(void* source) { throw null; } diff --git a/src/libraries/System.Runtime.CompilerServices.Unsafe/src/System.Runtime.CompilerServices.Unsafe.il b/src/libraries/System.Runtime.CompilerServices.Unsafe/src/System.Runtime.CompilerServices.Unsafe.il index 79736eb332e99..51e4105a138f3 100644 --- a/src/libraries/System.Runtime.CompilerServices.Unsafe/src/System.Runtime.CompilerServices.Unsafe.il +++ b/src/libraries/System.Runtime.CompilerServices.Unsafe/src/System.Runtime.CompilerServices.Unsafe.il @@ -469,6 +469,15 @@ ret } // end of method Unsafe::IsNullRef + .method public hidebysig static !!T& NullRef() cil managed aggressiveinlining + { + .custom instance void System.Runtime.Versioning.NonVersionableAttribute::.ctor() = ( 01 00 00 00 ) + .maxstack 1 + ldc.i4.0 + conv.u + ret + } // end of method Unsafe::NullRef + } // end of class System.Runtime.CompilerServices.Unsafe .class private auto ansi sealed beforefieldinit System.Runtime.Versioning.NonVersionableAttribute diff --git a/src/libraries/System.Runtime.CompilerServices.Unsafe/src/System.Runtime.CompilerServices.Unsafe.xml b/src/libraries/System.Runtime.CompilerServices.Unsafe/src/System.Runtime.CompilerServices.Unsafe.xml index 597212b2f8cab..c8f02fe55e3f5 100644 --- a/src/libraries/System.Runtime.CompilerServices.Unsafe/src/System.Runtime.CompilerServices.Unsafe.xml +++ b/src/libraries/System.Runtime.CompilerServices.Unsafe/src/System.Runtime.CompilerServices.Unsafe.xml @@ -240,6 +240,11 @@ The reference to check. This check is conceptually similar to "(void*)(&source) == nullptr". + + + Returns a by-ref to type that is a null reference. + + Copies bytes from the source address to the destination address. diff --git a/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs b/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs index bf9e54060ef52..b2af77a3dd984 100644 --- a/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs +++ b/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs @@ -1023,6 +1023,45 @@ public static unsafe void IsNullRef_Null() Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); } + + [Fact] + public static void NullRef() + { + // Validate that calling with primitive types works. + Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); + + Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); + + Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); + + Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); + + Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); + + Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); + + Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); + + Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); + + Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); + + Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); + + // Validate that calling on user-defined unmanaged structs works. + Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); + + Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); + + Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); + + Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); + + // Validate that calling on reference types works. + Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); + + Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); + } } [StructLayout(LayoutKind.Explicit)] From 90ed2c00e28a1c18d32569f2e38e990c0b58082c Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 28 Jul 2020 15:34:04 +0200 Subject: [PATCH 3/7] Minor code style tweaks --- .../tests/UnsafeTests.cs | 34 +++++-------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs b/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs index b2af77a3dd984..db41af056a28a 100644 --- a/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs +++ b/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs @@ -936,6 +936,7 @@ public static void SkipInit_PreservesPrevious() public static void IsNullRef_NotNull() { // Validate that calling with primitive types works. + sbyte sbyteValue = 1; Assert.False(Unsafe.IsNullRef(ref sbyteValue)); @@ -967,6 +968,7 @@ public static void IsNullRef_NotNull() Assert.False(Unsafe.IsNullRef(ref doubleValue)); // Validate that calling on user-defined unmanaged structs works. + Byte4 byte4Value = default; Assert.False(Unsafe.IsNullRef(ref byte4Value)); @@ -980,6 +982,7 @@ public static void IsNullRef_NotNull() Assert.False(Unsafe.IsNullRef(ref int32DoubleValue)); // Validate that calling on reference types works. + object objectValue = new object(); Assert.False(Unsafe.IsNullRef(ref objectValue)); @@ -991,36 +994,27 @@ public static void IsNullRef_NotNull() public static unsafe void IsNullRef_Null() { // Validate that calling with primitive types works. - Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); + Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); - Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); - Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); - Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); - Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); - Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); - Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); - Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); // Validate that calling on user-defined unmanaged structs works. - Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); + Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); - Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); - Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); // Validate that calling on reference types works. - Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); + Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); } @@ -1028,38 +1022,28 @@ public static unsafe void IsNullRef_Null() public static void NullRef() { // Validate that calling with primitive types works. - Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); + Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); - Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); - Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); - Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); - Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); - Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); - Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); - Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); - Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); // Validate that calling on user-defined unmanaged structs works. - Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); + Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); - Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); - Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); // Validate that calling on reference types works. - Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); + Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); } } From 1e4f670efc48e0fff3adf23237df650201f8c9dd Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 28 Jul 2020 17:01:12 +0200 Subject: [PATCH 4/7] Fixed a refactoring issue Co-authored-by: Adeel Mujahid --- .../System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs b/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs index db41af056a28a..72b3cc53c5baa 100644 --- a/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs +++ b/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs @@ -986,7 +986,7 @@ public static void IsNullRef_NotNull() object objectValue = new object(); Assert.False(Unsafe.IsNullRef(ref objectValue)); - string stringValue = nameof(IsNullRef); + string stringValue = nameof(IsNullRef_NotNull); Assert.False(Unsafe.IsNullRef(ref stringValue)); } From 1f20ca6188ffe41ed56c2046f368d4098e9ff01e Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 28 Jul 2020 17:13:50 +0200 Subject: [PATCH 5/7] Removed unnecessary test cases --- .../tests/UnsafeTests.cs | 71 ++----------------- 1 file changed, 6 insertions(+), 65 deletions(-) diff --git a/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs b/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs index 72b3cc53c5baa..d577380370cdf 100644 --- a/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs +++ b/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs @@ -413,7 +413,7 @@ public static void ByteOffsetStackByte4() public static unsafe void AsRef() { byte[] b = new byte[4] { 0x42, 0x42, 0x42, 0x42 }; - fixed (byte * p = b) + fixed (byte* p = b) { ref int r = ref Unsafe.AsRef(p); Assert.Equal(0x42424242, r); @@ -839,7 +839,7 @@ public static void SkipInit() Unsafe.SkipInit(out double doubleValue); // Validate that calling on user-defined unmanaged structs works. - + Unsafe.SkipInit(out Byte4 byte4Value); Unsafe.SkipInit(out Byte4Short2 byte4Short2Value); Unsafe.SkipInit(out Byte512 byte512Value); @@ -935,49 +935,13 @@ public static void SkipInit_PreservesPrevious() [Fact] public static void IsNullRef_NotNull() { - // Validate that calling with primitive types works. - - sbyte sbyteValue = 1; - Assert.False(Unsafe.IsNullRef(ref sbyteValue)); - - byte byteValue = 2; - Assert.False(Unsafe.IsNullRef(ref byteValue)); - - short shortValue = 3; - Assert.False(Unsafe.IsNullRef(ref shortValue)); - - ushort ushortValue = 4; - Assert.False(Unsafe.IsNullRef(ref ushortValue)); + // Validate that calling with a primitive type works. int intValue = 5; Assert.False(Unsafe.IsNullRef(ref intValue)); - uint uintValue = 6; - Assert.False(Unsafe.IsNullRef(ref uintValue)); - - long longValue = 7; - Assert.False(Unsafe.IsNullRef(ref longValue)); - - ulong ulongValue = 8; - Assert.False(Unsafe.IsNullRef(ref ulongValue)); - - float floatValue = 9; - Assert.False(Unsafe.IsNullRef(ref floatValue)); - - double doubleValue = 10; - Assert.False(Unsafe.IsNullRef(ref doubleValue)); - // Validate that calling on user-defined unmanaged structs works. - Byte4 byte4Value = default; - Assert.False(Unsafe.IsNullRef(ref byte4Value)); - - Byte4Short2 byte4Short2Value = default; - Assert.False(Unsafe.IsNullRef(ref byte4Short2Value)); - - Byte512 byte512Value = default; - Assert.False(Unsafe.IsNullRef(ref byte512Value)); - Int32Double int32DoubleValue = default; Assert.False(Unsafe.IsNullRef(ref int32DoubleValue)); @@ -993,23 +957,12 @@ public static void IsNullRef_NotNull() [Fact] public static unsafe void IsNullRef_Null() { - // Validate that calling with primitive types works. + // Validate that calling with a primitive type works. - Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); - Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); - Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); - Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); - Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); - Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); - Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); - Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); // Validate that calling on user-defined unmanaged structs works. - Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); - Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); - Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); // Validate that calling on reference types works. @@ -1021,24 +974,12 @@ public static unsafe void IsNullRef_Null() [Fact] public static void NullRef() { - // Validate that calling with primitive types works. + // Validate that calling with a primitive type works. - Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); - Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); - Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); - Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); - Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); - Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); - Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); - Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); - Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); // Validate that calling on user-defined unmanaged structs works. - Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); - Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); - Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); // Validate that calling on reference types works. @@ -1083,7 +1024,7 @@ public unsafe struct Byte512 public fixed byte Bytes[512]; } - [StructLayout(LayoutKind.Explicit, Size=16)] + [StructLayout(LayoutKind.Explicit, Size = 16)] public unsafe struct Int32Double { [FieldOffset(0)] From 7db0e115322223c3d3045a94a54c4171ca6a1737 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 28 Jul 2020 17:15:02 +0200 Subject: [PATCH 6/7] Fix consistency in XML docs Co-authored-by: Jan Kotas --- .../src/System.Runtime.CompilerServices.Unsafe.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Runtime.CompilerServices.Unsafe/src/System.Runtime.CompilerServices.Unsafe.xml b/src/libraries/System.Runtime.CompilerServices.Unsafe/src/System.Runtime.CompilerServices.Unsafe.xml index c8f02fe55e3f5..1979a5cb97052 100644 --- a/src/libraries/System.Runtime.CompilerServices.Unsafe/src/System.Runtime.CompilerServices.Unsafe.xml +++ b/src/libraries/System.Runtime.CompilerServices.Unsafe/src/System.Runtime.CompilerServices.Unsafe.xml @@ -235,14 +235,14 @@ - Returns if a given by-ref to type is a null reference. + Returns if a given reference to a value of type is a null reference. The reference to check. This check is conceptually similar to "(void*)(&source) == nullptr". - Returns a by-ref to type that is a null reference. + Returns a reference to a value of type that is a null reference. From 33e3e87f28a9c8d4aafbe1742f1fd032065da576 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 28 Jul 2020 17:38:35 +0200 Subject: [PATCH 7/7] Add more tests for null refs --- .../tests/UnsafeTests.cs | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs b/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs index d577380370cdf..34b0140aa23cf 100644 --- a/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs +++ b/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs @@ -933,7 +933,7 @@ public static void SkipInit_PreservesPrevious() } [Fact] - public static void IsNullRef_NotNull() + public static unsafe void IsNullRef_NotNull() { // Validate that calling with a primitive type works. @@ -952,6 +952,11 @@ public static void IsNullRef_NotNull() string stringValue = nameof(IsNullRef_NotNull); Assert.False(Unsafe.IsNullRef(ref stringValue)); + + // Validate on ref created from a pointer + + int* p = (int*)1; + Assert.False(Unsafe.IsNullRef(ref Unsafe.AsRef(p))); } [Fact] @@ -969,10 +974,15 @@ public static unsafe void IsNullRef_Null() Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(null))); + + // Validate on ref created from a pointer + + int* p = (int*)0; + Assert.True(Unsafe.IsNullRef(ref Unsafe.AsRef(p))); } [Fact] - public static void NullRef() + public static unsafe void NullRef() { // Validate that calling with a primitive type works. @@ -986,6 +996,18 @@ public static void NullRef() Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); Assert.True(Unsafe.IsNullRef(ref Unsafe.NullRef())); + + // Validate that pinning results in a null pointer + + fixed (void* p = &Unsafe.NullRef()) + { + Assert.True(p == (void*)0); + } + + // Validate that dereferencing a null ref throws a NullReferenceException + + Assert.Throws(() => Unsafe.NullRef() = 42); + Assert.Throws(() => Unsafe.NullRef()); } }