Change Span fields to private readonly + other review feedback
authorJan Kotas <jkotas@microsoft.com>
Sun, 30 Oct 2016 19:30:18 +0000 (12:30 -0700)
committerJan Kotas <jkotas@microsoft.com>
Mon, 31 Oct 2016 16:47:23 +0000 (09:47 -0700)
Commit migrated from https://github.com/dotnet/coreclr/commit/c9a481181ea74d3a642d27df1f22404ed73da140

src/coreclr/src/mscorlib/src/System.Private.CoreLib.txt
src/coreclr/src/mscorlib/src/System/ReadOnlySpan.cs
src/coreclr/src/mscorlib/src/System/Runtime/CompilerServices/Unsafe.cs
src/coreclr/src/mscorlib/src/System/Runtime/CompilerServices/jithelpers.cs
src/coreclr/src/mscorlib/src/System/Span.cs
src/coreclr/src/mscorlib/src/mscorlib.txt
src/coreclr/src/vm/jitinterface.cpp
src/coreclr/src/vm/mscorlib.h
src/coreclr/tests/src/CoreMangLib/system/span/BasicSpanTest.cs

index a3b567a..d1eb712 100644 (file)
@@ -635,7 +635,7 @@ Argument_NativeOverlappedWrongBoundHandle = 'overlapped' was not allocated by th
 Argument_NativeOverlappedAlreadyFree = 'overlapped' has already been freed.
 Argument_AlreadyBoundOrSyncHandle = 'handle' has already been bound to the thread pool, or was not opened for asynchronous I/O.
 #if FEATURE_SPAN_OF_T
-Argument_InvalidTypeWithPointersNotSupported = Cannot use type '{0}'. Only value types without pointers are supported.
+Argument_InvalidTypeWithPointersNotSupported = Cannot use type '{0}'. Only value types without pointers or references are supported.
 #endif // FEATURE_SPAN_OF_T
 
 ;
index b15ae24..2538fa4 100644 (file)
@@ -9,17 +9,16 @@ using System.Runtime.CompilerServices;
 namespace System
 {
     /// <summary>
-    /// ReadOnlySpan is a uniform API for dealing with arrays and subarrays, strings
-    /// and substrings, and unmanaged memory buffers.  It adds minimal overhead
-    /// to regular accesses and is a struct so that creation and subslicing do
-    /// not require additional allocations.  It is type- and memory-safe.
+    /// ReadOnlySpan represents contiguous read-only region of arbitrary memory, with performance
+    /// characteristics on par with T[]. Unlike arrays, it can point to either managed
+    /// or native memory, or to memory allocated on the stack. It is type- and memory-safe.
     /// </summary>
-    public struct ReadOnlySpan<T>
+    public unsafe struct ReadOnlySpan<T>
     {
         /// <summary>A byref or a native ptr. Do not access directly</summary>
-        internal /* readonly */ IntPtr _rawPointer;
+        private readonly IntPtr _rawPointer;
         /// <summary>The number of elements this ReadOnlySpan contains.</summary>
-        internal readonly int _length;
+        private readonly int _length;
 
         /// <summary>
         /// Creates a new span over the entirety of the target array.
@@ -32,7 +31,8 @@ namespace System
             if (array == null)
                 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
 
-            JitHelpers.SetByRef(out _rawPointer, ref JitHelpers.GetArrayData(array));
+            // TODO-SPAN: This has GC hole. It needs to be JIT intrinsic instead
+            _rawPointer = (IntPtr)Unsafe.AsPointer(ref JitHelpers.GetArrayData(array));
             _length = array.Length;
         }
 
@@ -52,10 +52,11 @@ namespace System
         {
             if (array == null)
                 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
-            if ((uint)start >= (uint)array.Length || (uint)length > (uint)(array.Length - start))
+            if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start))
                 ThrowHelper.ThrowArgumentOutOfRangeException();
 
-            JitHelpers.SetByRef(out _rawPointer, ref Unsafe.Add(ref JitHelpers.GetArrayData(array), start));
+            // TODO-SPAN: This has GC hole. It needs to be JIT intrinsic instead
+            _rawPointer = (IntPtr)Unsafe.AsPointer(ref Unsafe.Add(ref JitHelpers.GetArrayData(array), start));
             _length = length;
         }
 
@@ -74,32 +75,42 @@ namespace System
         /// Thrown when the specified <paramref name="length"/> is negative.
         /// </exception>
         [CLSCompliant(false)]
-        public unsafe ReadOnlySpan(void* ptr, int length)
+        public unsafe ReadOnlySpan(void* pointer, int length)
         {
             if (JitHelpers.ContainsReferences<T>())
                 ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
             if (length < 0)
                 ThrowHelper.ThrowArgumentOutOfRangeException();
 
-            _rawPointer = (IntPtr)ptr;
+            _rawPointer = (IntPtr)pointer;
             _length = length;
         }
 
         /// <summary>
-        /// An internal helper for creating spans. Not for public use.
+        /// An internal helper for creating spans.
         /// </summary>
         internal ReadOnlySpan(ref T ptr, int length)
         {
-            JitHelpers.SetByRef(out _rawPointer, ref ptr);
+            // TODO-SPAN: This has GC hole. It needs to be JIT intrinsic instead
+            _rawPointer = (IntPtr)Unsafe.AsPointer(ref ptr);
             _length = length;
         }
 
         /// <summary>
+        /// An internal helper for accessing spans.
+        /// </summary>
+        internal unsafe ref T GetRawPointer()
+        {
+            // TODO-SPAN: This has GC hole. It needs to be JIT intrinsic instead
+            return ref Unsafe.As<IntPtr, T>(ref *(IntPtr *)_rawPointer);
+        }
+
+        /// <summary>
         /// Defines an implicit conversion of a <see cref="Span{T}"/> to a <see cref="ReadOnlySpan{T}"/>
         /// </summary>
         public static implicit operator ReadOnlySpan<T>(Span<T> slice)
         {
-            return new ReadOnlySpan<T>(ref JitHelpers.GetByRef<T>(ref slice._rawPointer), slice._length);
+            return new ReadOnlySpan<T>(ref slice.GetRawPointer(), slice.Length);
         }
 
         /// <summary>
@@ -155,7 +166,7 @@ namespace System
                 if ((uint)index >= (uint)_length)
                     ThrowHelper.ThrowIndexOutOfRangeException();
 
-                return Unsafe.Add(ref JitHelpers.GetByRef<T>(ref _rawPointer), index);
+                return Unsafe.Add(ref GetRawPointer(), index);
             }
         }
 
@@ -170,7 +181,7 @@ namespace System
                 return Array.Empty<T>();
 
             var destination = new T[_length];
-            SpanHelper.CopyTo<T>(ref JitHelpers.GetArrayData(destination), ref JitHelpers.GetByRef<T>(ref _rawPointer), _length);
+            SpanHelper.CopyTo<T>(ref JitHelpers.GetArrayData(destination), ref GetRawPointer(), _length);
             return destination;
         }
 
@@ -186,7 +197,7 @@ namespace System
             if ((uint)start > (uint)_length)
                 ThrowHelper.ThrowArgumentOutOfRangeException();
 
-            return new ReadOnlySpan<T>(ref Unsafe.Add(ref JitHelpers.GetByRef<T>(ref _rawPointer), start), _length - start);
+            return new ReadOnlySpan<T>(ref Unsafe.Add(ref GetRawPointer(), start), _length - start);
         }
 
         /// <summary>
@@ -199,10 +210,10 @@ namespace System
         /// </exception>
         public ReadOnlySpan<T> Slice(int start, int length)
         {
-            if ((uint)start >= (uint)_length || (uint)length > (uint)(_length - start))
+            if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start))
                 ThrowHelper.ThrowArgumentOutOfRangeException();
 
-            return new ReadOnlySpan<T>(ref Unsafe.Add(ref JitHelpers.GetByRef<T>(ref _rawPointer), start), length);
+            return new ReadOnlySpan<T>(ref Unsafe.Add(ref GetRawPointer(), start), length);
         }
 
         /// <summary>
@@ -211,8 +222,8 @@ namespace System
         /// </summary>
         public bool Equals(ReadOnlySpan<T> other)
         {
-            return (_length == other._length) &&
-                (_length == 0 || Unsafe.AreSame(ref JitHelpers.GetByRef<T>(ref _rawPointer), ref JitHelpers.GetByRef<T>(ref other._rawPointer)));
+            return (_length == other.Length) &&
+                (_length == 0 || Unsafe.AreSame(ref GetRawPointer(), ref other.GetRawPointer()));
         }
 
         /// <summary>
@@ -222,10 +233,10 @@ namespace System
         /// <param name="destination">The span to copy items into.</param>
         public bool TryCopyTo(Span<T> destination)
         {
-            if (_length > destination._length)
+            if ((uint)_length > (uint)destination.Length)
                 return false;
 
-            SpanHelper.CopyTo<T>(ref JitHelpers.GetByRef<T>(ref destination._rawPointer), ref JitHelpers.GetByRef<T>(ref _rawPointer), _length);
+            SpanHelper.CopyTo<T>(ref destination.GetRawPointer(), ref GetRawPointer(), _length);
             return true;
         }
     }
@@ -281,7 +292,7 @@ namespace System
         {
             if (text == null)
                 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text);
-            if ((uint)start >= (uint)text.Length || (uint)length > (uint)(text.Length - start))
+            if ((uint)start > (uint)text.Length || (uint)length > (uint)(text.Length - start))
                 ThrowHelper.ThrowArgumentOutOfRangeException();
 
             return new ReadOnlySpan<char>(ref Unsafe.Add(ref text.GetFirstCharRef(), start), length);
index 3ebc321..70d8ec0 100644 (file)
@@ -14,9 +14,21 @@ namespace System.Runtime.CompilerServices
     /// <summary>
     /// Contains generic, low-level functionality for manipulating pointers.
     /// </summary>
-    internal static class Unsafe
+    internal static unsafe class Unsafe
     {
         /// <summary>
+        /// Returns a pointer to the given by-ref parameter.
+        /// </summary>
+        [NonVersionable]
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static void* AsPointer<T>(ref T value)
+        {
+            // The body of this function will be replaced by the EE with unsafe code that just returns sizeof !!T
+            // See getILIntrinsicImplementationForUnsafe for how this happens.  
+            throw new InvalidOperationException();
+        }
+
+        /// <summary>
         /// Returns the size of an object of the given type parameter.
         /// </summary>
         [NonVersionable]
@@ -24,7 +36,7 @@ namespace System.Runtime.CompilerServices
         public static int SizeOf<T>()
         {
             // The body of this function will be replaced by the EE with unsafe code that just returns sizeof !!T
-            // See getILIntrinsicImplementation for how this happens.  
+            // See getILIntrinsicImplementationForUnsafe for how this happens.  
             throw new InvalidOperationException();
         }
 
@@ -36,7 +48,7 @@ namespace System.Runtime.CompilerServices
         public static ref TTo As<TFrom, TTo>(ref TFrom source)
         {
             // The body of this function will be replaced by the EE with unsafe code that just returns sizeof !!T
-            // See getILIntrinsicImplementation for how this happens.  
+            // See getILIntrinsicImplementationForUnsafe for how this happens.  
             throw new InvalidOperationException();
         }
 
@@ -48,7 +60,7 @@ namespace System.Runtime.CompilerServices
         public static ref T Add<T>(ref T source, int elementOffset)
         {
             // The body of this function will be replaced by the EE with unsafe code!!!
-            // See getILIntrinsicImplementation for how this happens.
+            // See getILIntrinsicImplementationForUnsafe for how this happens.
             typeof(T).ToString(); // Type used by the actual method body
             throw new InvalidOperationException();
         }
@@ -61,7 +73,7 @@ namespace System.Runtime.CompilerServices
         public static bool AreSame<T>(ref T left, ref T right)
         {
             // The body of this function will be replaced by the EE with unsafe code!!!
-            // See getILIntrinsicImplementation for how this happens.  
+            // See getILIntrinsicImplementationForUnsafe for how this happens.  
             throw new InvalidOperationException();
         }
     }
index 1ad7899..a4c67aa 100644 (file)
@@ -228,31 +228,18 @@ namespace System.Runtime.CompilerServices {
 #endif
 
 #if FEATURE_SPAN_OF_T
-        static internal ref T GetByRef<T>(ref IntPtr byref)
-        {
-            // The body of this function will be replaced by the EE with unsafe code!!!
-            // See getILIntrinsicImplementation for how this happens.
-            throw new InvalidOperationException();
-        }
-
-        static internal void SetByRef<T>(out IntPtr byref, ref T value)
-        {
-            // The body of this function will be replaced by the EE with unsafe code!!!
-            // See getILIntrinsicImplementation for how this happens.
-            throw new InvalidOperationException();
-        }
-
         static internal bool ByRefLessThan<T>(ref T refA, ref T refB)
         {
             // The body of this function will be replaced by the EE with unsafe code!!!
-            // See getILIntrinsicImplementation for how this happens.  
+            // See getILIntrinsicImplementation for how this happens.
             throw new InvalidOperationException();
         }
 
         /// <returns>true if given type is reference type or value type that contains references</returns>
         static internal bool ContainsReferences<T>()
         {
-            // See getILIntrinsicImplementation for how this happens.  
+            // The body of this function will be replaced by the EE with unsafe code!!!
+            // See getILIntrinsicImplementation for how this happens.
             throw new InvalidOperationException();
         }
 
index d70dc31..ec95511 100644 (file)
@@ -9,17 +9,16 @@ using System.Runtime.CompilerServices;
 namespace System
 {
     /// <summary>
-    /// Span is a uniform API for dealing with arrays and subarrays, strings
-    /// and substrings, and unmanaged memory buffers.  It adds minimal overhead
-    /// to regular accesses and is a struct so that creation and subslicing do
-    /// not require additional allocations.  It is type- and memory-safe.
+    /// Span represents contiguous region of arbitrary memory, with performance
+    /// characteristics on par with T[]. Unlike arrays, it can point to either managed
+    /// or native memory, or to memory allocated on the stack. It is type- and memory-safe.
     /// </summary>
-    public struct Span<T>
+    public unsafe struct Span<T>
     {
         /// <summary>A byref or a native ptr. Do not access directly</summary>
-        internal /* readonly */ IntPtr _rawPointer;
+        private readonly IntPtr _rawPointer;
         /// <summary>The number of elements this Span contains.</summary>
-        internal readonly int _length;
+        private readonly int _length;
 
         /// <summary>
         /// Creates a new span over the entirety of the target array.
@@ -37,7 +36,8 @@ namespace System
                     ThrowHelper.ThrowArrayTypeMismatchException();
             }
 
-            JitHelpers.SetByRef(out _rawPointer, ref JitHelpers.GetArrayData(array));
+            // TODO-SPAN: This has GC hole. It needs to be JIT intrinsic instead
+            _rawPointer = (IntPtr)Unsafe.AsPointer(ref JitHelpers.GetArrayData(array));
             _length = array.Length;
         }
 
@@ -62,10 +62,11 @@ namespace System
                 if (array.GetType() != typeof(T[]))
                     ThrowHelper.ThrowArrayTypeMismatchException();
             }
-            if ((uint)start >= (uint)array.Length || (uint)length > (uint)(array.Length - start))
+            if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start))
                 ThrowHelper.ThrowArgumentOutOfRangeException();
 
-            JitHelpers.SetByRef(out _rawPointer, ref Unsafe.Add(ref JitHelpers.GetArrayData(array), start));
+            // TODO-SPAN: This has GC hole. It needs to be JIT intrinsic instead
+            _rawPointer = (IntPtr)Unsafe.AsPointer(ref Unsafe.Add(ref JitHelpers.GetArrayData(array), start));
             _length = length;
         }
 
@@ -84,27 +85,37 @@ namespace System
         /// Thrown when the specified <paramref name="length"/> is negative.
         /// </exception>
         [CLSCompliant(false)]
-        public unsafe Span(void* ptr, int length)
+        public unsafe Span(void* pointer, int length)
         {
             if (JitHelpers.ContainsReferences<T>())
                 ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
             if (length < 0)
                 ThrowHelper.ThrowArgumentOutOfRangeException();
 
-            _rawPointer = (IntPtr)ptr;
+            _rawPointer = (IntPtr)pointer;
             _length = length;
         }
 
         /// <summary>
-        /// An internal helper for creating spans. Not for public use.
+        /// An internal helper for creating spans.
         /// </summary>
         internal Span(ref T ptr, int length)
         {
-            JitHelpers.SetByRef(out _rawPointer, ref ptr);
+            // TODO-SPAN: This has GC hole. It needs to be JIT intrinsic instead
+            _rawPointer = (IntPtr)Unsafe.AsPointer(ref ptr);
             _length = length;
         }
 
         /// <summary>
+        /// An internal helper for accessing spans.
+        /// </summary>
+        internal unsafe ref T GetRawPointer()
+        {
+            // TODO-SPAN: This has GC hole. It needs to be JIT intrinsic instead
+            return ref Unsafe.As<IntPtr, T>(ref *(IntPtr*)_rawPointer);
+        }
+
+        /// <summary>
         /// Defines an implicit conversion of an array to a <see cref="Span{T}"/>
         /// </summary>
         public static implicit operator Span<T>(T[] array)
@@ -157,14 +168,14 @@ namespace System
                 if ((uint)index >= (uint)_length)
                     ThrowHelper.ThrowIndexOutOfRangeException();
 
-                return Unsafe.Add(ref JitHelpers.GetByRef<T>(ref _rawPointer), index);
+                return Unsafe.Add(ref GetRawPointer(), index);
             }
             set
             {
                 if ((uint)index >= (uint)_length)
                     ThrowHelper.ThrowIndexOutOfRangeException();
 
-                Unsafe.Add(ref JitHelpers.GetByRef<T>(ref _rawPointer), index) = value;
+                Unsafe.Add(ref GetRawPointer(), index) = value;
             }
         }
 
@@ -179,7 +190,7 @@ namespace System
                 return Array.Empty<T>();
 
             var destination = new T[_length];
-            SpanHelper.CopyTo<T>(ref JitHelpers.GetArrayData(destination), ref JitHelpers.GetByRef<T>(ref _rawPointer), _length);
+            SpanHelper.CopyTo<T>(ref JitHelpers.GetArrayData(destination), ref GetRawPointer(), _length);
             return destination;
         }
 
@@ -195,7 +206,7 @@ namespace System
             if ((uint)start > (uint)_length)
                 ThrowHelper.ThrowArgumentOutOfRangeException();
 
-            return new Span<T>(ref Unsafe.Add(ref JitHelpers.GetByRef<T>(ref _rawPointer), start), _length - start);
+            return new Span<T>(ref Unsafe.Add(ref GetRawPointer(), start), _length - start);
         }
 
         /// <summary>
@@ -208,10 +219,10 @@ namespace System
         /// </exception>
         public Span<T> Slice(int start, int length)
         {
-            if ((uint)start >= (uint)_length || (uint)length > (uint)(_length - start))
+            if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start))
                 ThrowHelper.ThrowArgumentOutOfRangeException();
 
-            return new Span<T>(ref Unsafe.Add(ref JitHelpers.GetByRef<T>(ref _rawPointer), start), length);
+            return new Span<T>(ref Unsafe.Add(ref GetRawPointer(), start), length);
         }
 
         /// <summary>
@@ -220,8 +231,8 @@ namespace System
         /// </summary>
         public bool Equals(Span<T> other)
         {
-            return (_length == other._length) &&
-                (_length == 0 || Unsafe.AreSame(ref JitHelpers.GetByRef<T>(ref _rawPointer), ref JitHelpers.GetByRef<T>(ref other._rawPointer)));
+            return (_length == other.Length) &&
+                (_length == 0 || Unsafe.AreSame(ref GetRawPointer(), ref other.GetRawPointer()));
         }
 
         /// <summary>
@@ -231,10 +242,10 @@ namespace System
         /// <param name="destination">The span to copy items into.</param>
         public bool TryCopyTo(Span<T> destination)
         {
-            if (Length > destination.Length)
+            if ((uint)_length > (uint)destination.Length)
                 return false;
 
-            SpanHelper.CopyTo<T>(ref JitHelpers.GetByRef<T>(ref destination._rawPointer), ref JitHelpers.GetByRef<T>(ref _rawPointer), _length);
+            SpanHelper.CopyTo<T>(ref destination.GetRawPointer(), ref GetRawPointer(), _length);
             return true;
         }
 
@@ -243,10 +254,10 @@ namespace System
         /// </exception>
         public void Set(ReadOnlySpan<T> values)
         {
-            if ((uint)values._length > (uint)_length)
+            if ((uint)values.Length > (uint)_length)
                 ThrowHelper.ThrowArgumentOutOfRangeException();
 
-            SpanHelper.CopyTo<T>(ref JitHelpers.GetByRef<T>(ref _rawPointer), ref JitHelpers.GetByRef<T>(ref values._rawPointer), values._length);
+            SpanHelper.CopyTo<T>(ref GetRawPointer(), ref values.GetRawPointer(), values.Length);
         }
     }
 
@@ -254,7 +265,7 @@ namespace System
     {
         /// <summary>
         /// Casts a Span of one primitive type <typeparamref name="T"/> to Span of bytes.
-        /// That type may not contain pointers. This is checked at runtime in order to preserve type safety.
+        /// That type may not contain pointers or references. This is checked at runtime in order to preserve type safety.
         /// </summary>
         /// <param name="source">The source slice, of type <typeparamref name="T"/>.</param>
         /// <exception cref="System.ArgumentException">
@@ -267,13 +278,13 @@ namespace System
                 ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
 
             return new Span<byte>(
-                ref JitHelpers.GetByRef<byte>(ref source._rawPointer),
-                checked((int)(source._length * Unsafe.SizeOf<T>())));
+                ref Unsafe.As<T, byte>(ref source.GetRawPointer()),
+                checked(source.Length * Unsafe.SizeOf<T>()));
         }
 
         /// <summary>
         /// Casts a ReadOnlySpan of one primitive type <typeparamref name="T"/> to ReadOnlySpan of bytes.
-        /// That type may not contain pointers. This is checked at runtime in order to preserve type safety.
+        /// That type may not contain pointers or references. This is checked at runtime in order to preserve type safety.
         /// </summary>
         /// <param name="source">The source slice, of type <typeparamref name="T"/>.</param>
         /// <exception cref="System.ArgumentException">
@@ -286,13 +297,13 @@ namespace System
                 ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
 
             return new ReadOnlySpan<byte>(
-                ref JitHelpers.GetByRef<byte>(ref source._rawPointer),
-                checked((int)(source._length * Unsafe.SizeOf<T>())));
+                ref Unsafe.As<T, byte>(ref source.GetRawPointer()),
+                checked(source.Length * Unsafe.SizeOf<T>()));
         }
 
         /// <summary>
         /// Casts a Span of one primitive type <typeparamref name="TFrom"/> to another primitive type <typeparamref name="TTo"/>.
-        /// These types may not contain pointers. This is checked at runtime in order to preserve type safety.
+        /// These types may not contain pointers or references. This is checked at runtime in order to preserve type safety.
         /// </summary>
         /// <remarks>
         /// Supported only for platforms that support misaligned memory access.
@@ -311,13 +322,13 @@ namespace System
                 ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TTo));
 
             return new Span<TTo>(
-                ref JitHelpers.GetByRef<TTo>(ref source._rawPointer),
-                checked((int)(source._length * Unsafe.SizeOf<TFrom>() / Unsafe.SizeOf<TTo>())));
+                ref Unsafe.As<TFrom, TTo>(ref source.GetRawPointer()),
+                checked((int)((long)source.Length * Unsafe.SizeOf<TFrom>() / Unsafe.SizeOf<TTo>())));
         }
 
         /// <summary>
         /// Casts a ReadOnlySpan of one primitive type <typeparamref name="TFrom"/> to another primitive type <typeparamref name="TTo"/>.
-        /// These types may not contain pointers. This is checked at runtime in order to preserve type safety.
+        /// These types may not contain pointers or references. This is checked at runtime in order to preserve type safety.
         /// </summary>
         /// <remarks>
         /// Supported only for platforms that support misaligned memory access.
@@ -336,8 +347,8 @@ namespace System
                 ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TTo));
 
             return new ReadOnlySpan<TTo>(
-                ref JitHelpers.GetByRef<TTo>(ref source._rawPointer),
-                checked((int)(source._length * Unsafe.SizeOf<TFrom>() / Unsafe.SizeOf<TTo>())));
+                ref Unsafe.As<TFrom, TTo>(ref source.GetRawPointer()),
+                checked((int)((long)source.Length * Unsafe.SizeOf<TFrom>() / Unsafe.SizeOf<TTo>())));
         }
     }
 
index 5e19ea8..7574417 100644 (file)
@@ -632,7 +632,7 @@ Argument_UnrecognizedLoaderOptimization = Unrecognized LOADER_OPTIMIZATION prope
 ArgumentException_NotAllCustomSortingFuncsDefined = Implementations of all the NLS functions must be provided.
 ArgumentException_MinSortingVersion = The runtime does not support a version of "{0}" less than {1}.
 #if FEATURE_SPAN_OF_T
-Argument_InvalidTypeWithPointersNotSupported = Cannot use type '{0}'. Only value types without pointers are supported.
+Argument_InvalidTypeWithPointersNotSupported = Cannot use type '{0}'. Only value types without pointers or references are supported.
 #endif // FEATURE_SPAN_OF_T
 
 
index f7730dd..6163daf 100644 (file)
@@ -6912,28 +6912,6 @@ bool getILIntrinsicImplementation(MethodDesc * ftn,
         }
     }
 #ifdef FEATURE_SPAN_OF_T
-    else if (tk == MscorlibBinder::GetMethod(METHOD__JIT_HELPERS__GET_BYREF)->GetMemberDef())
-    {
-        // TODO-SPAN: This has potential GC hole. It needs to be JIT intrinsic instead
-        static const BYTE ilcode[] = { CEE_LDARG_0, CEE_LDIND_I, CEE_RET };
-        methInfo->ILCode = const_cast<BYTE*>(ilcode);
-        methInfo->ILCodeSize = sizeof(ilcode);
-        methInfo->maxStack = 1;
-        methInfo->EHcount = 0;
-        methInfo->options = (CorInfoOptions)0;
-        return true;
-    }
-    else if (tk == MscorlibBinder::GetMethod(METHOD__JIT_HELPERS__SET_BYREF)->GetMemberDef())
-    {
-        // TODO-SPAN: This has potential GC hole. It needs to be JIT intrinsic instead
-        static const BYTE ilcode[] = { CEE_LDARG_0, CEE_LDARG_1, CEE_STIND_I, CEE_RET };
-        methInfo->ILCode = const_cast<BYTE*>(ilcode);
-        methInfo->ILCodeSize = sizeof(ilcode);
-        methInfo->maxStack = 2;
-        methInfo->EHcount = 0;
-        methInfo->options = (CorInfoOptions)0;
-        return true;
-    }
     else if (tk == MscorlibBinder::GetMethod(METHOD__JIT_HELPERS__BYREF_LESSTHAN)->GetMemberDef())
     {
         // Compare the two arguments
@@ -7008,6 +6986,17 @@ bool getILIntrinsicImplementationForUnsafe(MethodDesc * ftn,
 
     mdMethodDef tk = ftn->GetMemberDef();
 
+    if (tk == MscorlibBinder::GetMethod(METHOD__UNSAFE__AS_POINTER)->GetMemberDef())
+    {
+        // Return the argument that was passed in.
+        static const BYTE ilcode[] = { CEE_LDARG_0, CEE_CONV_U, CEE_RET };
+        methInfo->ILCode = const_cast<BYTE*>(ilcode);
+        methInfo->ILCodeSize = sizeof(ilcode);
+        methInfo->maxStack = 1;
+        methInfo->EHcount = 0;
+        methInfo->options = (CorInfoOptions)0;
+        return true;
+    }
     if (tk == MscorlibBinder::GetMethod(METHOD__UNSAFE__SIZEOF)->GetMemberDef())
     {
         _ASSERTE(ftn->HasMethodInstantiation());
index 7d75e59..4bfa656 100644 (file)
@@ -1345,8 +1345,6 @@ DEFINE_METHOD(JIT_HELPERS,          UNSAFE_ENUM_CAST_LONG,  UnsafeEnumCastLong,
 DEFINE_METHOD(JIT_HELPERS,          UNSAFE_CAST_TO_STACKPTR,UnsafeCastToStackPointer, NoSig)
 #endif // _DEBUG
 #ifdef FEATURE_SPAN_OF_T
-DEFINE_METHOD(JIT_HELPERS,          GET_BYREF,              GetByRef, NoSig)
-DEFINE_METHOD(JIT_HELPERS,          SET_BYREF,              SetByRef, NoSig)
 DEFINE_METHOD(JIT_HELPERS,          BYREF_LESSTHAN,         ByRefLessThan, NoSig)
 DEFINE_METHOD(JIT_HELPERS,          GET_ARRAY_DATA,         GetArrayData, NoSig)
 DEFINE_METHOD(JIT_HELPERS,          CONTAINSREFERENCES,     ContainsReferences, NoSig)
@@ -1354,6 +1352,7 @@ DEFINE_METHOD(JIT_HELPERS,          CONTAINSREFERENCES,     ContainsReferences,
 
 #ifdef FEATURE_SPAN_OF_T
 DEFINE_CLASS(UNSAFE,                CompilerServices,       Unsafe)
+DEFINE_METHOD(UNSAFE,               AS_POINTER,             AsPointer, NoSig)
 DEFINE_METHOD(UNSAFE,               SIZEOF,                 SizeOf, NoSig)
 DEFINE_METHOD(UNSAFE,               BYREF_AS,               As, NoSig)
 DEFINE_METHOD(UNSAFE,               BYREF_ADD,              Add, NoSig)
index a141eaf..7b0be90 100644 (file)
@@ -42,6 +42,8 @@ class My
 
         Test(CanAccessItemsViaIndexer, "CanAccessItemsViaIndexer", ref failedTestsCount);
 
+        Test(TestBoundaryEmptySpan, "TestBoundaryEmptySpan", ref failedTestsCount);
+
         Test(ReferenceTypesAreSupported, "ReferenceTypesAreSupported", ref failedTestsCount);
 
         Test(CanUpdateUnderlyingArray, "CanUpdateUnderlyingArray", ref failedTestsCount);
@@ -89,6 +91,17 @@ class My
         AssertTrue(Sum(subslice) == 5, "Failed to sum subslice");
     }
 
+    static TestBoundaryEmptySpan()
+    {
+        int[] a = new byte[5];
+
+        Span<int> slice = new Span<int>(a, a.Length, 0);
+        AssertEqual(slice.Length, 0);
+
+        Span<int> subSlice = new Span<int>(a).Slice(a.Length, 0);
+        AssertEqual(subSlice.Length, 0);
+    }
+
     static void ReferenceTypesAreSupported()
     {
         var underlyingArray = new ReferenceType[] { new ReferenceType(0), new ReferenceType(1), new ReferenceType(2) };
@@ -112,7 +125,7 @@ class My
         }
         catch (System.ArgumentException ex)
         {
-            AssertTrue(ex.Message == "Cannot use type 'ValueTypeWithPointers'. Only value types without pointers are supported.",
+            AssertTrue(ex.Message == "Cannot use type 'ValueTypeWithPointers'. Only value types without pointers or references are supported.",
                 "Exception message is incorrect");
         }
 
@@ -123,7 +136,7 @@ class My
         }
         catch (System.ArgumentException ex)
         {
-            AssertTrue(ex.Message == "Cannot use type 'ReferenceType'. Only value types without pointers are supported.",
+            AssertTrue(ex.Message == "Cannot use type 'ReferenceType'. Only value types without pointers or references are supported.",
                 "Exception message is incorrect");
         }
     }
@@ -497,7 +510,7 @@ class My
         }
         catch (System.ArgumentException ex)
         {
-            AssertTrue(ex.Message == "Cannot use type 'ValueTypeWithPointers'. Only value types without pointers are supported.",
+            AssertTrue(ex.Message == "Cannot use type 'ValueTypeWithPointers'. Only value types without pointers or references are supported.",
                 "Exception message is incorrect");
         }
 
@@ -508,7 +521,7 @@ class My
         }
         catch (System.ArgumentException ex)
         {
-            AssertTrue(ex.Message == "Cannot use type 'ValueTypeWithPointers'. Only value types without pointers are supported.",
+            AssertTrue(ex.Message == "Cannot use type 'ValueTypeWithPointers'. Only value types without pointers or references are supported.",
                 "Exception message is incorrect");
         }
 
@@ -520,7 +533,7 @@ class My
         }
         catch (System.ArgumentException ex)
         {
-            AssertTrue(ex.Message == "Cannot use type 'ValueTypeWithPointers'. Only value types without pointers are supported.",
+            AssertTrue(ex.Message == "Cannot use type 'ValueTypeWithPointers'. Only value types without pointers or references are supported.",
                 "Exception message is incorrect");
         }
     }