[runtime] Fix Empty generic enumerator equality
authorAlexander Kyte <alexmkyte@gmail.com>
Mon, 2 Oct 2017 18:59:16 +0000 (14:59 -0400)
committerMarek Safar <marek.safar@gmail.com>
Fri, 6 Oct 2017 09:35:17 +0000 (11:35 +0200)
On the CLR, two enumerators containing zero elements will
have reference equality if they are inflated with the same
generic type.

If comparing two enumerators which are not inflated (Array.Empty, for
instance), they are not equal when they contain zero elements.

This reference equality behavior is not compatible with treating
the enumerators as structs, as value types will not have reference
equality.

mcs/class/corlib/System/Array.cs
mcs/class/corlib/Test/System/ArrayTest.cs

index d699446f2b5ce3aad52382b8900a6bde0a3161bd..91424bc6f8f6dfbf4d2c5457e453f4db01add388 100644 (file)
@@ -68,7 +68,10 @@ namespace System
 
                internal IEnumerator<T> InternalArray__IEnumerable_GetEnumerator<T> ()
                {
 
                internal IEnumerator<T> InternalArray__IEnumerable_GetEnumerator<T> ()
                {
-                       return new InternalEnumerator<T> (this);
+                       if (Length == 0)
+                               return EmptyInternalEnumerator<T>.Value;
+                       else
+                               return new InternalEnumerator<T> (this);
                }
 
                internal void InternalArray__ICollection_Clear ()
                }
 
                internal void InternalArray__ICollection_Clear ()
@@ -271,6 +274,38 @@ namespace System
                        }
                }
 
                        }
                }
 
+               internal class EmptyInternalEnumerator<T> : IEnumerator<T>
+               {
+                       public static readonly EmptyInternalEnumerator<T> Value = new EmptyInternalEnumerator<T> ();
+
+                       public void Dispose ()
+                       {
+                               return;
+                       }
+
+                       public bool MoveNext ()
+                       {
+                               return false;
+                       }
+
+                       public T Current {
+                               get {
+                                       throw new InvalidOperationException ("Enumeration has not started. Call MoveNext");
+                               }
+                       }
+
+                       object IEnumerator.Current {
+                               get {
+                                       return Current;
+                               }
+                       }
+
+                       void IEnumerator.Reset ()
+                       {
+                               return;
+                       }
+               }
+
                // InternalCall Methods
                [MethodImplAttribute (MethodImplOptions.InternalCall)]
                extern int GetRank ();
                // InternalCall Methods
                [MethodImplAttribute (MethodImplOptions.InternalCall)]
                extern int GetRank ();
index 0a04041aea34c055fdd0dd984c56be97ee6eedb8..36db33d6352b9ac3efc47122a7897ed4289ea21d 100644 (file)
@@ -3693,6 +3693,20 @@ public class ArrayTest
                Assert.AreEqual (3, c.Counter);         
        }
 
                Assert.AreEqual (3, c.Counter);         
        }
 
+       [Test]
+       public void EnumeratorsEquality ()
+       {
+               int [] normalBase = new int [0];
+               IEnumerable<int> specialBase = new int [0];
+
+               var firstSpecial = specialBase.GetEnumerator ();
+               var secondSpecial = specialBase.GetEnumerator ();
+               var firstNormal = normalBase.GetEnumerator ();
+               var secondNormal = normalBase.GetEnumerator ();
+
+               Assert.IsFalse (object.ReferenceEquals (firstNormal, secondNormal));
+               Assert.IsTrue (object.ReferenceEquals (firstSpecial, secondSpecial));
+       }
 
        [Test]
        public void JaggedArrayCtor ()
 
        [Test]
        public void JaggedArrayCtor ()