Add Unsafe.IsNullRef and Unsafe.NullRef (#40008)
authorSergio Pedri <sergio0694@live.com>
Wed, 29 Jul 2020 03:06:52 +0000 (05:06 +0200)
committerGitHub <noreply@github.com>
Wed, 29 Jul 2020 03:06:52 +0000 (20:06 -0700)
Co-authored-by: Adeel Mujahid <adeelbm@outlook.com>
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
src/libraries/System.Runtime.CompilerServices.Unsafe/ref/System.Runtime.CompilerServices.Unsafe.cs
src/libraries/System.Runtime.CompilerServices.Unsafe/src/System.Runtime.CompilerServices.Unsafe.il
src/libraries/System.Runtime.CompilerServices.Unsafe/src/System.Runtime.CompilerServices.Unsafe.xml
src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs

index ae977a0..99c28c3 100644 (file)
@@ -35,6 +35,8 @@ namespace System.Runtime.CompilerServices
         public static unsafe void InitBlockUnaligned(void* startAddress, byte value, uint byteCount) { }
         public static bool IsAddressGreaterThan<T>(ref T left, ref T right) { throw null; }
         public static bool IsAddressLessThan<T>(ref T left, ref T right) { throw null; }
+        public static bool IsNullRef<T>(ref T source) { throw null; }
+        public static ref T NullRef<T>() { throw null; }
         public static T ReadUnaligned<T>(ref byte source) { throw null; }
         public static unsafe T ReadUnaligned<T>(void* source) { throw null; }
         public static unsafe T Read<T>(void* source) { throw null; }
index 8740faf..51e4105 100644 (file)
         ret
   } // end of method Unsafe::IsAddressLessThan
 
+  .method public hidebysig static bool IsNullRef<T>(!!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
+
+  .method public hidebysig static !!T& NullRef<T>() 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
index 1aef90c..1979a5c 100644 (file)
             or the objects being referenced must both be pinned; or both references must represent unmanaged pointers; otherwise the result is undefined.
             </remarks>
         </member>
+        <member name="M:System.Runtime.CompilerServices.Unsafe.IsNullRef``1(``0@)">
+          <summary>
+            Returns if a given reference to a value of type <typeparamref name="T"/> is a null reference.
+          </summary>
+          <param name="source">The reference to check.</param>
+          <remarks>This check is conceptually similar to "(void*)(&amp;source) == nullptr".</remarks>
+        </member>
+        <member name="M:System.Runtime.CompilerServices.Unsafe.NullRef``1">
+          <summary>
+            Returns a reference to a value of type <typeparamref name="T"/> that is a null reference.
+          </summary>
+        </member>
         <member name="M:System.Runtime.CompilerServices.Unsafe.CopyBlock(System.Void*,System.Void*,System.UInt32)">
             <summary>
             Copies bytes from the source address to the destination address.
index 8847352..34b0140 100644 (file)
@@ -413,7 +413,7 @@ namespace System.Runtime.CompilerServices
         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<int>(p);
                 Assert.Equal(0x42424242, r);
@@ -839,7 +839,7 @@ namespace System.Runtime.CompilerServices
             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);
@@ -931,6 +931,84 @@ namespace System.Runtime.CompilerServices
             Unsafe.SkipInit(out stringValue);
             Assert.Equal("25", stringValue);
         }
+
+        [Fact]
+        public static unsafe void IsNullRef_NotNull()
+        {
+            // Validate that calling with a primitive type works.
+
+            int intValue = 5;
+            Assert.False(Unsafe.IsNullRef<int>(ref intValue));
+
+            // Validate that calling on user-defined unmanaged structs works.
+
+            Int32Double int32DoubleValue = default;
+            Assert.False(Unsafe.IsNullRef<Int32Double>(ref int32DoubleValue));
+
+            // Validate that calling on reference types works.
+
+            object objectValue = new object();
+            Assert.False(Unsafe.IsNullRef<object>(ref objectValue));
+
+            string stringValue = nameof(IsNullRef_NotNull);
+            Assert.False(Unsafe.IsNullRef<string>(ref stringValue));
+
+            // Validate on ref created from a pointer
+
+            int* p = (int*)1;
+            Assert.False(Unsafe.IsNullRef<int>(ref Unsafe.AsRef<int>(p)));
+        }
+
+        [Fact]
+        public static unsafe void IsNullRef_Null()
+        {
+            // Validate that calling with a primitive type works.
+
+            Assert.True(Unsafe.IsNullRef<int>(ref Unsafe.AsRef<int>(null)));
+
+            // Validate that calling on user-defined unmanaged structs works.
+
+            Assert.True(Unsafe.IsNullRef<Int32Double>(ref Unsafe.AsRef<Int32Double>(null)));
+
+            // Validate that calling on reference types works.
+
+            Assert.True(Unsafe.IsNullRef<object>(ref Unsafe.AsRef<object>(null)));
+            Assert.True(Unsafe.IsNullRef<string>(ref Unsafe.AsRef<string>(null)));
+
+            // Validate on ref created from a pointer
+
+            int* p = (int*)0;
+            Assert.True(Unsafe.IsNullRef<int>(ref Unsafe.AsRef<int>(p)));
+        }
+
+        [Fact]
+        public static unsafe void NullRef()
+        {
+            // Validate that calling with a primitive type works.
+
+            Assert.True(Unsafe.IsNullRef<int>(ref Unsafe.NullRef<int>()));
+
+            // Validate that calling on user-defined unmanaged structs works.
+
+            Assert.True(Unsafe.IsNullRef<Int32Double>(ref Unsafe.NullRef<Int32Double>()));
+
+            // Validate that calling on reference types works.
+
+            Assert.True(Unsafe.IsNullRef<object>(ref Unsafe.NullRef<object>()));
+            Assert.True(Unsafe.IsNullRef<string>(ref Unsafe.NullRef<string>()));
+
+            // Validate that pinning results in a null pointer
+
+            fixed (void* p = &Unsafe.NullRef<int>())
+            {
+                Assert.True(p == (void*)0);
+            }
+
+            // Validate that dereferencing a null ref throws a NullReferenceException
+
+            Assert.Throws<NullReferenceException>(() => Unsafe.NullRef<int>() = 42);
+            Assert.Throws<NullReferenceException>(() => Unsafe.NullRef<int>());
+        }
     }
 
     [StructLayout(LayoutKind.Explicit)]
@@ -968,7 +1046,7 @@ namespace System.Runtime.CompilerServices
         public fixed byte Bytes[512];
     }
 
-    [StructLayout(LayoutKind.Explicit, Size=16)]
+    [StructLayout(LayoutKind.Explicit, Size = 16)]
     public unsafe struct Int32Double
     {
         [FieldOffset(0)]