From 15ce34d83fa9cf6e6baa1324e8726c9564377c37 Mon Sep 17 00:00:00 2001 From: DoctorKrolic Date: Thu, 1 Aug 2024 15:15:25 +0300 Subject: [PATCH 1/8] Use manual `foreach` instead of `AddRange` method when collection type of spreading element doesn't implement `ICollection`, but has a `struct` enumerator --- .../LocalRewriter_CollectionExpression.cs | 15 + .../Semantics/CollectionExpressionTests.cs | 513 ++++++++++++++++++ 2 files changed, 528 insertions(+) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs index 812443b7804fd..57301d74865a8 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs @@ -1070,6 +1070,21 @@ private BoundExpression CreateAndPopulateList(BoundCollectionExpression node, Ty var type = rewrittenSpreadOperand.Type!; var useSiteInfo = GetNewCompoundUseSiteInfo(); + + if (spreadElement.EnumeratorInfoOpt is { } enumeratorInfo) + { + var iCollectionOfTType = _compilation.GetSpecialType(SpecialType.System_Collections_Generic_ICollection_T); + var iCollectionOfElementType = iCollectionOfTType.Construct(enumeratorInfo.ElementType); + + // If collection has a struct enumerator but doesn't implement ICollection + // then manual `foreach` is always more efficient then using `AddRange` method + if (enumeratorInfo.GetEnumeratorInfo.Method.ReturnType.IsValueType && + !enumeratorInfo.CollectionType.ImplementsInterface(iCollectionOfElementType, ref useSiteInfo)) + { + return false; + } + } + var conversion = _compilation.Conversions.ClassifyConversionFromType(type, addRangeMethod.Parameters[0].Type, isChecked: false, ref useSiteInfo); _diagnostics.Add(rewrittenSpreadOperand.Syntax, useSiteInfo); if (conversion.IsIdentity || (conversion.IsImplicit && conversion.IsReference)) diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs index 500653be1255e..8da0f120b9dd4 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs @@ -34640,6 +34640,519 @@ .locals init (System.Collections.Generic.List V_0, """); } + [Fact] + public void List_SingleSpread_CustomCollection_NotICollectionAndNoStructEnumerator() + { + var source = """ + using System.Collections; + using System.Collections.Generic; + + class MyCollection(List list) : IEnumerable + { + public IEnumerator GetEnumerator() => list.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + + class C + { + static void Main() + { + M(new([1, 2, 3])).Report(); + } + + static List M(MyCollection c) => [..c]; + } + """; + + var verifier = CompileAndVerify([source, s_collectionExtensionsWithSpan], expectedOutput: IncludeExpectedOutput("[1, 2, 3],"), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("C.M", """ + { + // Code size 13 (0xd) + .maxstack 3 + IL_0000: newobj "System.Collections.Generic.List..ctor()" + IL_0005: dup + IL_0006: ldarg.0 + IL_0007: callvirt "void System.Collections.Generic.List.AddRange(System.Collections.Generic.IEnumerable)" + IL_000c: ret + } + """); + } + + [Fact] + public void List_SingleSpread_CustomCollection_ICollectionAndNoStructEnumerator() + { + var source = """ + using System.Collections; + using System.Collections.Generic; + + class MyCollection(List list) : ICollection + { + public int Count => list.Count; + + public bool IsReadOnly => false; + + public void Add(int item) => list.Add(item); + + public void Clear() => list.Clear(); + + public bool Contains(int item) => list.Contains(item); + + public void CopyTo(int[] array, int arrayIndex) => list.CopyTo(array, arrayIndex); + + public bool Remove(int item) => list.Remove(item); + + public IEnumerator GetEnumerator() => list.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + + class C + { + static void Main() + { + M(new([1, 2, 3])).Report(); + } + + static List M(MyCollection c) => [..c]; + } + """; + + var verifier = CompileAndVerify([source, s_collectionExtensionsWithSpan], expectedOutput: IncludeExpectedOutput("[1, 2, 3],"), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("C.M", """ + { + // Code size 89 (0x59) + .maxstack 3 + .locals init (int V_0, + System.Collections.Generic.List V_1, + System.Span V_2, + int V_3, + System.Collections.Generic.IEnumerator V_4, + int V_5) + IL_0000: ldarg.0 + IL_0001: dup + IL_0002: callvirt "int MyCollection.Count.get" + IL_0007: stloc.0 + IL_0008: ldloc.0 + IL_0009: newobj "System.Collections.Generic.List..ctor(int)" + IL_000e: stloc.1 + IL_000f: ldloc.1 + IL_0010: ldloc.0 + IL_0011: call "void System.Runtime.InteropServices.CollectionsMarshal.SetCount(System.Collections.Generic.List, int)" + IL_0016: ldloc.1 + IL_0017: call "System.Span System.Runtime.InteropServices.CollectionsMarshal.AsSpan(System.Collections.Generic.List)" + IL_001c: stloc.2 + IL_001d: ldc.i4.0 + IL_001e: stloc.3 + IL_001f: callvirt "System.Collections.Generic.IEnumerator MyCollection.GetEnumerator()" + IL_0024: stloc.s V_4 + .try + { + IL_0026: br.s IL_0040 + IL_0028: ldloc.s V_4 + IL_002a: callvirt "int System.Collections.Generic.IEnumerator.Current.get" + IL_002f: stloc.s V_5 + IL_0031: ldloca.s V_2 + IL_0033: ldloc.3 + IL_0034: call "ref int System.Span.this[int].get" + IL_0039: ldloc.s V_5 + IL_003b: stind.i4 + IL_003c: ldloc.3 + IL_003d: ldc.i4.1 + IL_003e: add + IL_003f: stloc.3 + IL_0040: ldloc.s V_4 + IL_0042: callvirt "bool System.Collections.IEnumerator.MoveNext()" + IL_0047: brtrue.s IL_0028 + IL_0049: leave.s IL_0057 + } + finally + { + IL_004b: ldloc.s V_4 + IL_004d: brfalse.s IL_0056 + IL_004f: ldloc.s V_4 + IL_0051: callvirt "void System.IDisposable.Dispose()" + IL_0056: endfinally + } + IL_0057: ldloc.1 + IL_0058: ret + } + """); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74615")] + public void List_SingleSpread_CustomCollection_NotICollectionAndStructEnumerator() + { + var source = """ + using System.Collections; + using System.Collections.Generic; + + class MyCollection(List list) : IEnumerable + { + public Enumerator GetEnumerator() => new(list.GetEnumerator()); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public struct Enumerator(List.Enumerator enumerator) : IEnumerator + { + public int Current => enumerator.Current; + + object IEnumerator.Current => Current; + + public bool MoveNext() => enumerator.MoveNext(); + + public void Dispose() => enumerator.Dispose(); + + public void Reset() { } + } + } + + class C + { + static void Main() + { + M(new([1, 2, 3])).Report(); + } + + static List M(MyCollection c) => [..c]; + } + """; + + var verifier = CompileAndVerify([source, s_collectionExtensionsWithSpan], expectedOutput: IncludeExpectedOutput("[1, 2, 3],"), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("C.M", """ + { + // Code size 57 (0x39) + .maxstack 2 + .locals init (System.Collections.Generic.List V_0, + MyCollection.Enumerator V_1, + int V_2) + IL_0000: newobj "System.Collections.Generic.List..ctor()" + IL_0005: stloc.0 + IL_0006: ldarg.0 + IL_0007: callvirt "MyCollection.Enumerator MyCollection.GetEnumerator()" + IL_000c: stloc.1 + .try + { + IL_000d: br.s IL_001e + IL_000f: ldloca.s V_1 + IL_0011: call "int MyCollection.Enumerator.Current.get" + IL_0016: stloc.2 + IL_0017: ldloc.0 + IL_0018: ldloc.2 + IL_0019: callvirt "void System.Collections.Generic.List.Add(int)" + IL_001e: ldloca.s V_1 + IL_0020: call "bool MyCollection.Enumerator.MoveNext()" + IL_0025: brtrue.s IL_000f + IL_0027: leave.s IL_0037 + } + finally + { + IL_0029: ldloca.s V_1 + IL_002b: constrained. "MyCollection.Enumerator" + IL_0031: callvirt "void System.IDisposable.Dispose()" + IL_0036: endfinally + } + IL_0037: ldloc.0 + IL_0038: ret + } + """); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74615")] + public void List_SingleSpread_CustomCollection_NotICollectionAndStructEnumerator_MissingICollectionOfTType() + { + var source = """ + using System.Collections; + using System.Collections.Generic; + + class MyCollection(List list) : IEnumerable + { + public Enumerator GetEnumerator() => new(list.GetEnumerator()); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public struct Enumerator(List.Enumerator enumerator) : IEnumerator + { + public int Current => enumerator.Current; + + object IEnumerator.Current => Current; + + public bool MoveNext() => enumerator.MoveNext(); + + public void Dispose() => enumerator.Dispose(); + + public void Reset() { } + } + } + + class C + { + static void Main() + { + M(new([1, 2, 3])).Report(); + } + + static List M(MyCollection c) => [..c]; + } + """; + + var comp = CreateCompilation([source, s_collectionExtensionsWithSpan], options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net80); + comp.MakeTypeMissing(SpecialType.System_Collections_Generic_ICollection_T); + + var verifier = CompileAndVerify(comp, expectedOutput: IncludeExpectedOutput("[1, 2, 3],"), verify: Verification.Skipped); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("C.M", """ + { + // Code size 57 (0x39) + .maxstack 2 + .locals init (System.Collections.Generic.List V_0, + MyCollection.Enumerator V_1, + int V_2) + IL_0000: newobj "System.Collections.Generic.List..ctor()" + IL_0005: stloc.0 + IL_0006: ldarg.0 + IL_0007: callvirt "MyCollection.Enumerator MyCollection.GetEnumerator()" + IL_000c: stloc.1 + .try + { + IL_000d: br.s IL_001e + IL_000f: ldloca.s V_1 + IL_0011: call "int MyCollection.Enumerator.Current.get" + IL_0016: stloc.2 + IL_0017: ldloc.0 + IL_0018: ldloc.2 + IL_0019: callvirt "void System.Collections.Generic.List.Add(int)" + IL_001e: ldloca.s V_1 + IL_0020: call "bool MyCollection.Enumerator.MoveNext()" + IL_0025: brtrue.s IL_000f + IL_0027: leave.s IL_0037 + } + finally + { + IL_0029: ldloca.s V_1 + IL_002b: constrained. "MyCollection.Enumerator" + IL_0031: callvirt "void System.IDisposable.Dispose()" + IL_0036: endfinally + } + IL_0037: ldloc.0 + IL_0038: ret + } + """); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74615")] + public void List_SingleSpread_CustomCollection_NotICollectionAndStructEnumerator_MixedWithOtherAddRangeSpread() + { + var source = """ + using System.Collections; + using System.Collections.Generic; + + class MyCollection(List list) : IEnumerable + { + public Enumerator GetEnumerator() => new(list.GetEnumerator()); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public struct Enumerator(List.Enumerator enumerator) : IEnumerator + { + public int Current => enumerator.Current; + + object IEnumerator.Current => Current; + + public bool MoveNext() => enumerator.MoveNext(); + + public void Dispose() => enumerator.Dispose(); + + public void Reset() { } + } + } + + class C + { + static void Main() + { + M(new([1, 2, 3]), [4, 5, 6]).Report(); + } + + static List M(MyCollection c, IEnumerable e) => [..c, ..e]; + } + """; + + var verifier = CompileAndVerify([source, s_collectionExtensionsWithSpan], expectedOutput: IncludeExpectedOutput("[1, 2, 3, 4, 5, 6],"), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("C.M", """ + { + // Code size 64 (0x40) + .maxstack 2 + .locals init (System.Collections.Generic.List V_0, + MyCollection.Enumerator V_1, + int V_2) + IL_0000: newobj "System.Collections.Generic.List..ctor()" + IL_0005: stloc.0 + IL_0006: ldarg.0 + IL_0007: callvirt "MyCollection.Enumerator MyCollection.GetEnumerator()" + IL_000c: stloc.1 + .try + { + IL_000d: br.s IL_001e + IL_000f: ldloca.s V_1 + IL_0011: call "int MyCollection.Enumerator.Current.get" + IL_0016: stloc.2 + IL_0017: ldloc.0 + IL_0018: ldloc.2 + IL_0019: callvirt "void System.Collections.Generic.List.Add(int)" + IL_001e: ldloca.s V_1 + IL_0020: call "bool MyCollection.Enumerator.MoveNext()" + IL_0025: brtrue.s IL_000f + IL_0027: leave.s IL_0037 + } + finally + { + IL_0029: ldloca.s V_1 + IL_002b: constrained. "MyCollection.Enumerator" + IL_0031: callvirt "void System.IDisposable.Dispose()" + IL_0036: endfinally + } + IL_0037: ldloc.0 + IL_0038: ldarg.1 + IL_0039: callvirt "void System.Collections.Generic.List.AddRange(System.Collections.Generic.IEnumerable)" + IL_003e: ldloc.0 + IL_003f: ret + } + """); + } + + [Fact] + public void List_SingleSpread_CustomCollection_ICollectionAndStructEnumerator() + { + var source = """ + using System.Collections; + using System.Collections.Generic; + + class MyCollection(List list) : ICollection + { + public int Count => list.Count; + + public bool IsReadOnly => false; + + public void Add(int item) => list.Add(item); + + public void Clear() => list.Clear(); + + public bool Contains(int item) => list.Contains(item); + + public void CopyTo(int[] array, int arrayIndex) => list.CopyTo(array, arrayIndex); + + public bool Remove(int item) => list.Remove(item); + + public Enumerator GetEnumerator() => new(list.GetEnumerator()); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public struct Enumerator(List.Enumerator enumerator) : IEnumerator + { + public int Current => enumerator.Current; + + object IEnumerator.Current => Current; + + public bool MoveNext() => enumerator.MoveNext(); + + public void Dispose() => enumerator.Dispose(); + + public void Reset() { } + } + } + + class C + { + static void Main() + { + M(new([1, 2, 3])).Report(); + } + + static List M(MyCollection c) => [..c]; + } + """; + + var verifier = CompileAndVerify([source, s_collectionExtensionsWithSpan], expectedOutput: IncludeExpectedOutput("[1, 2, 3],"), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("C.M", """ + { + // Code size 91 (0x5b) + .maxstack 3 + .locals init (int V_0, + System.Collections.Generic.List V_1, + System.Span V_2, + int V_3, + MyCollection.Enumerator V_4, + int V_5) + IL_0000: ldarg.0 + IL_0001: dup + IL_0002: callvirt "int MyCollection.Count.get" + IL_0007: stloc.0 + IL_0008: ldloc.0 + IL_0009: newobj "System.Collections.Generic.List..ctor(int)" + IL_000e: stloc.1 + IL_000f: ldloc.1 + IL_0010: ldloc.0 + IL_0011: call "void System.Runtime.InteropServices.CollectionsMarshal.SetCount(System.Collections.Generic.List, int)" + IL_0016: ldloc.1 + IL_0017: call "System.Span System.Runtime.InteropServices.CollectionsMarshal.AsSpan(System.Collections.Generic.List)" + IL_001c: stloc.2 + IL_001d: ldc.i4.0 + IL_001e: stloc.3 + IL_001f: callvirt "MyCollection.Enumerator MyCollection.GetEnumerator()" + IL_0024: stloc.s V_4 + .try + { + IL_0026: br.s IL_0040 + IL_0028: ldloca.s V_4 + IL_002a: call "int MyCollection.Enumerator.Current.get" + IL_002f: stloc.s V_5 + IL_0031: ldloca.s V_2 + IL_0033: ldloc.3 + IL_0034: call "ref int System.Span.this[int].get" + IL_0039: ldloc.s V_5 + IL_003b: stind.i4 + IL_003c: ldloc.3 + IL_003d: ldc.i4.1 + IL_003e: add + IL_003f: stloc.3 + IL_0040: ldloca.s V_4 + IL_0042: call "bool MyCollection.Enumerator.MoveNext()" + IL_0047: brtrue.s IL_0028 + IL_0049: leave.s IL_0059 + } + finally + { + IL_004b: ldloca.s V_4 + IL_004d: constrained. "MyCollection.Enumerator" + IL_0053: callvirt "void System.IDisposable.Dispose()" + IL_0058: endfinally + } + IL_0059: ldloc.1 + IL_005a: ret + } + """); + } + [Fact] public void List_AddRange_IEnumerable() { From 08b9ea8a6525534f64b019e83151afd46002db75 Mon Sep 17 00:00:00 2001 From: DoctorKrolic Date: Thu, 1 Aug 2024 17:37:39 +0300 Subject: [PATCH 2/8] Tweak test name --- .../CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs index 8da0f120b9dd4..0125fdf91f5e9 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs @@ -34952,7 +34952,7 @@ .locals init (System.Collections.Generic.List V_0, } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74615")] - public void List_SingleSpread_CustomCollection_NotICollectionAndStructEnumerator_MixedWithOtherAddRangeSpread() + public void List_CustomCollection_NotICollectionAndStructEnumerator_MixedWithOtherAddRangeSpread() { var source = """ using System.Collections; From 0ac9d3cbe4f5e8b7f076f7aad27eff5715d053b8 Mon Sep 17 00:00:00 2001 From: DoctorKrolic Date: Mon, 5 Aug 2024 21:49:42 +0300 Subject: [PATCH 3/8] Discarded use-site info --- .../LocalRewriter/LocalRewriter_CollectionExpression.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs index 57301d74865a8..39b2a27934197 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs @@ -1069,22 +1069,22 @@ private BoundExpression CreateAndPopulateList(BoundCollectionExpression node, Ty var type = rewrittenSpreadOperand.Type!; - var useSiteInfo = GetNewCompoundUseSiteInfo(); - if (spreadElement.EnumeratorInfoOpt is { } enumeratorInfo) { var iCollectionOfTType = _compilation.GetSpecialType(SpecialType.System_Collections_Generic_ICollection_T); var iCollectionOfElementType = iCollectionOfTType.Construct(enumeratorInfo.ElementType); + var discardedUseSiteInfo = CompoundUseSiteInfo.Discarded; // If collection has a struct enumerator but doesn't implement ICollection // then manual `foreach` is always more efficient then using `AddRange` method if (enumeratorInfo.GetEnumeratorInfo.Method.ReturnType.IsValueType && - !enumeratorInfo.CollectionType.ImplementsInterface(iCollectionOfElementType, ref useSiteInfo)) + !enumeratorInfo.CollectionType.ImplementsInterface(iCollectionOfElementType, ref discardedUseSiteInfo)) { return false; } } + var useSiteInfo = GetNewCompoundUseSiteInfo(); var conversion = _compilation.Conversions.ClassifyConversionFromType(type, addRangeMethod.Parameters[0].Type, isChecked: false, ref useSiteInfo); _diagnostics.Add(rewrittenSpreadOperand.Syntax, useSiteInfo); if (conversion.IsIdentity || (conversion.IsImplicit && conversion.IsReference)) From b534c2d1b9f96a84857cadd05c3b2521339ee15f Mon Sep 17 00:00:00 2001 From: DoctorKrolic Date: Wed, 14 Aug 2024 19:42:46 +0300 Subject: [PATCH 4/8] Move down --- .../LocalRewriter/LocalRewriter_CollectionExpression.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs index 39b2a27934197..8a0d981a006e3 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs @@ -1067,8 +1067,6 @@ private BoundExpression CreateAndPopulateList(BoundCollectionExpression node, Ty if (addRangeMethod is null) return false; - var type = rewrittenSpreadOperand.Type!; - if (spreadElement.EnumeratorInfoOpt is { } enumeratorInfo) { var iCollectionOfTType = _compilation.GetSpecialType(SpecialType.System_Collections_Generic_ICollection_T); @@ -1084,6 +1082,8 @@ private BoundExpression CreateAndPopulateList(BoundCollectionExpression node, Ty } } + var type = rewrittenSpreadOperand.Type!; + var useSiteInfo = GetNewCompoundUseSiteInfo(); var conversion = _compilation.Conversions.ClassifyConversionFromType(type, addRangeMethod.Parameters[0].Type, isChecked: false, ref useSiteInfo); _diagnostics.Add(rewrittenSpreadOperand.Syntax, useSiteInfo); From 67fce6e8899eef235c04c3c798b57395f3faf3c5 Mon Sep 17 00:00:00 2001 From: DoctorKrolic Date: Wed, 14 Aug 2024 19:55:37 +0300 Subject: [PATCH 5/8] Use collection expression extensions without span --- .../Emit2/Semantics/CollectionExpressionTests.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs index 0125fdf91f5e9..e2d98cdd5e9fb 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs @@ -34665,7 +34665,7 @@ static void Main() } """; - var verifier = CompileAndVerify([source, s_collectionExtensionsWithSpan], expectedOutput: IncludeExpectedOutput("[1, 2, 3],"), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); + var verifier = CompileAndVerify([source, s_collectionExtensions], expectedOutput: IncludeExpectedOutput("[1, 2, 3],"), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.M", """ @@ -34720,7 +34720,7 @@ static void Main() } """; - var verifier = CompileAndVerify([source, s_collectionExtensionsWithSpan], expectedOutput: IncludeExpectedOutput("[1, 2, 3],"), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); + var verifier = CompileAndVerify([source, s_collectionExtensions], expectedOutput: IncludeExpectedOutput("[1, 2, 3],"), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.M", """ @@ -34824,7 +34824,7 @@ static void Main() } """; - var verifier = CompileAndVerify([source, s_collectionExtensionsWithSpan], expectedOutput: IncludeExpectedOutput("[1, 2, 3],"), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); + var verifier = CompileAndVerify([source, s_collectionExtensions], expectedOutput: IncludeExpectedOutput("[1, 2, 3],"), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.M", """ @@ -34906,7 +34906,7 @@ static void Main() } """; - var comp = CreateCompilation([source, s_collectionExtensionsWithSpan], options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net80); + var comp = CreateCompilation([source, s_collectionExtensions], options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net80); comp.MakeTypeMissing(SpecialType.System_Collections_Generic_ICollection_T); var verifier = CompileAndVerify(comp, expectedOutput: IncludeExpectedOutput("[1, 2, 3],"), verify: Verification.Skipped); @@ -34991,7 +34991,7 @@ static void Main() } """; - var verifier = CompileAndVerify([source, s_collectionExtensionsWithSpan], expectedOutput: IncludeExpectedOutput("[1, 2, 3, 4, 5, 6],"), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); + var verifier = CompileAndVerify([source, s_collectionExtensions], expectedOutput: IncludeExpectedOutput("[1, 2, 3, 4, 5, 6],"), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.M", """ @@ -35090,7 +35090,7 @@ static void Main() } """; - var verifier = CompileAndVerify([source, s_collectionExtensionsWithSpan], expectedOutput: IncludeExpectedOutput("[1, 2, 3],"), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); + var verifier = CompileAndVerify([source, s_collectionExtensions], expectedOutput: IncludeExpectedOutput("[1, 2, 3],"), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.M", """ From 07ac41e2006c63c7dbd8497c2b3b815cba075a37 Mon Sep 17 00:00:00 2001 From: DoctorKrolic Date: Wed, 14 Aug 2024 20:15:22 +0300 Subject: [PATCH 6/8] Verify for multiple target frameworks --- .../Semantics/CollectionExpressionTests.cs | 296 ++++++++++-------- 1 file changed, 173 insertions(+), 123 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs index e2d98cdd5e9fb..2263f07958d30 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs @@ -34681,8 +34681,10 @@ .maxstack 3 """); } - [Fact] - public void List_SingleSpread_CustomCollection_ICollectionAndNoStructEnumerator() + [Theory] + [InlineData(TargetFramework.Net80)] + [InlineData(TargetFramework.Standard)] + public void List_SingleSpread_CustomCollection_ICollectionAndNoStructEnumerator(TargetFramework targetFramework) { var source = """ using System.Collections; @@ -34720,68 +34722,91 @@ static void Main() } """; - var verifier = CompileAndVerify([source, s_collectionExtensions], expectedOutput: IncludeExpectedOutput("[1, 2, 3],"), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); + var verifier = CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3],", targetFramework: targetFramework, verify: Verification.Skipped); verifier.VerifyDiagnostics(); - verifier.VerifyIL("C.M", """ - { - // Code size 89 (0x59) - .maxstack 3 - .locals init (int V_0, - System.Collections.Generic.List V_1, - System.Span V_2, - int V_3, - System.Collections.Generic.IEnumerator V_4, - int V_5) - IL_0000: ldarg.0 - IL_0001: dup - IL_0002: callvirt "int MyCollection.Count.get" - IL_0007: stloc.0 - IL_0008: ldloc.0 - IL_0009: newobj "System.Collections.Generic.List..ctor(int)" - IL_000e: stloc.1 - IL_000f: ldloc.1 - IL_0010: ldloc.0 - IL_0011: call "void System.Runtime.InteropServices.CollectionsMarshal.SetCount(System.Collections.Generic.List, int)" - IL_0016: ldloc.1 - IL_0017: call "System.Span System.Runtime.InteropServices.CollectionsMarshal.AsSpan(System.Collections.Generic.List)" - IL_001c: stloc.2 - IL_001d: ldc.i4.0 - IL_001e: stloc.3 - IL_001f: callvirt "System.Collections.Generic.IEnumerator MyCollection.GetEnumerator()" - IL_0024: stloc.s V_4 - .try - { - IL_0026: br.s IL_0040 - IL_0028: ldloc.s V_4 - IL_002a: callvirt "int System.Collections.Generic.IEnumerator.Current.get" - IL_002f: stloc.s V_5 - IL_0031: ldloca.s V_2 - IL_0033: ldloc.3 - IL_0034: call "ref int System.Span.this[int].get" - IL_0039: ldloc.s V_5 - IL_003b: stind.i4 - IL_003c: ldloc.3 - IL_003d: ldc.i4.1 - IL_003e: add - IL_003f: stloc.3 - IL_0040: ldloc.s V_4 - IL_0042: callvirt "bool System.Collections.IEnumerator.MoveNext()" - IL_0047: brtrue.s IL_0028 - IL_0049: leave.s IL_0057 - } - finally - { - IL_004b: ldloc.s V_4 - IL_004d: brfalse.s IL_0056 - IL_004f: ldloc.s V_4 - IL_0051: callvirt "void System.IDisposable.Dispose()" - IL_0056: endfinally - } - IL_0057: ldloc.1 - IL_0058: ret - } - """); + switch (targetFramework) + { + case TargetFramework.Net80: + verifier.VerifyIL("C.M", """ + { + // Code size 89 (0x59) + .maxstack 3 + .locals init (int V_0, + System.Collections.Generic.List V_1, + System.Span V_2, + int V_3, + System.Collections.Generic.IEnumerator V_4, + int V_5) + IL_0000: ldarg.0 + IL_0001: dup + IL_0002: callvirt "int MyCollection.Count.get" + IL_0007: stloc.0 + IL_0008: ldloc.0 + IL_0009: newobj "System.Collections.Generic.List..ctor(int)" + IL_000e: stloc.1 + IL_000f: ldloc.1 + IL_0010: ldloc.0 + IL_0011: call "void System.Runtime.InteropServices.CollectionsMarshal.SetCount(System.Collections.Generic.List, int)" + IL_0016: ldloc.1 + IL_0017: call "System.Span System.Runtime.InteropServices.CollectionsMarshal.AsSpan(System.Collections.Generic.List)" + IL_001c: stloc.2 + IL_001d: ldc.i4.0 + IL_001e: stloc.3 + IL_001f: callvirt "System.Collections.Generic.IEnumerator MyCollection.GetEnumerator()" + IL_0024: stloc.s V_4 + .try + { + IL_0026: br.s IL_0040 + IL_0028: ldloc.s V_4 + IL_002a: callvirt "int System.Collections.Generic.IEnumerator.Current.get" + IL_002f: stloc.s V_5 + IL_0031: ldloca.s V_2 + IL_0033: ldloc.3 + IL_0034: call "ref int System.Span.this[int].get" + IL_0039: ldloc.s V_5 + IL_003b: stind.i4 + IL_003c: ldloc.3 + IL_003d: ldc.i4.1 + IL_003e: add + IL_003f: stloc.3 + IL_0040: ldloc.s V_4 + IL_0042: callvirt "bool System.Collections.IEnumerator.MoveNext()" + IL_0047: brtrue.s IL_0028 + IL_0049: leave.s IL_0057 + } + finally + { + IL_004b: ldloc.s V_4 + IL_004d: brfalse.s IL_0056 + IL_004f: ldloc.s V_4 + IL_0051: callvirt "void System.IDisposable.Dispose()" + IL_0056: endfinally + } + IL_0057: ldloc.1 + IL_0058: ret + } + """); + break; + case TargetFramework.Standard: + verifier.VerifyIL("C.M", """ + { + // Code size 21 (0x15) + .maxstack 3 + .locals init (MyCollection V_0) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: callvirt "int MyCollection.Count.get" + IL_0008: newobj "System.Collections.Generic.List..ctor(int)" + IL_000d: dup + IL_000e: ldloc.0 + IL_000f: callvirt "void System.Collections.Generic.List.AddRange(System.Collections.Generic.IEnumerable)" + IL_0014: ret + } + """); + break; + } } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74615")] @@ -35036,8 +35061,10 @@ .locals init (System.Collections.Generic.List V_0, """); } - [Fact] - public void List_SingleSpread_CustomCollection_ICollectionAndStructEnumerator() + [Theory] + [InlineData(TargetFramework.Net80)] + [InlineData(TargetFramework.Standard)] + public void List_SingleSpread_CustomCollection_ICollectionAndStructEnumerator(TargetFramework targetFramework) { var source = """ using System.Collections; @@ -35090,67 +35117,90 @@ static void Main() } """; - var verifier = CompileAndVerify([source, s_collectionExtensions], expectedOutput: IncludeExpectedOutput("[1, 2, 3],"), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); + var verifier = CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3],", targetFramework: targetFramework, verify: Verification.Skipped); verifier.VerifyDiagnostics(); - verifier.VerifyIL("C.M", """ - { - // Code size 91 (0x5b) - .maxstack 3 - .locals init (int V_0, - System.Collections.Generic.List V_1, - System.Span V_2, - int V_3, - MyCollection.Enumerator V_4, - int V_5) - IL_0000: ldarg.0 - IL_0001: dup - IL_0002: callvirt "int MyCollection.Count.get" - IL_0007: stloc.0 - IL_0008: ldloc.0 - IL_0009: newobj "System.Collections.Generic.List..ctor(int)" - IL_000e: stloc.1 - IL_000f: ldloc.1 - IL_0010: ldloc.0 - IL_0011: call "void System.Runtime.InteropServices.CollectionsMarshal.SetCount(System.Collections.Generic.List, int)" - IL_0016: ldloc.1 - IL_0017: call "System.Span System.Runtime.InteropServices.CollectionsMarshal.AsSpan(System.Collections.Generic.List)" - IL_001c: stloc.2 - IL_001d: ldc.i4.0 - IL_001e: stloc.3 - IL_001f: callvirt "MyCollection.Enumerator MyCollection.GetEnumerator()" - IL_0024: stloc.s V_4 - .try - { - IL_0026: br.s IL_0040 - IL_0028: ldloca.s V_4 - IL_002a: call "int MyCollection.Enumerator.Current.get" - IL_002f: stloc.s V_5 - IL_0031: ldloca.s V_2 - IL_0033: ldloc.3 - IL_0034: call "ref int System.Span.this[int].get" - IL_0039: ldloc.s V_5 - IL_003b: stind.i4 - IL_003c: ldloc.3 - IL_003d: ldc.i4.1 - IL_003e: add - IL_003f: stloc.3 - IL_0040: ldloca.s V_4 - IL_0042: call "bool MyCollection.Enumerator.MoveNext()" - IL_0047: brtrue.s IL_0028 - IL_0049: leave.s IL_0059 - } - finally - { - IL_004b: ldloca.s V_4 - IL_004d: constrained. "MyCollection.Enumerator" - IL_0053: callvirt "void System.IDisposable.Dispose()" - IL_0058: endfinally - } - IL_0059: ldloc.1 - IL_005a: ret - } - """); + switch (targetFramework) + { + case TargetFramework.Net80: + verifier.VerifyIL("C.M", """ + { + // Code size 91 (0x5b) + .maxstack 3 + .locals init (int V_0, + System.Collections.Generic.List V_1, + System.Span V_2, + int V_3, + MyCollection.Enumerator V_4, + int V_5) + IL_0000: ldarg.0 + IL_0001: dup + IL_0002: callvirt "int MyCollection.Count.get" + IL_0007: stloc.0 + IL_0008: ldloc.0 + IL_0009: newobj "System.Collections.Generic.List..ctor(int)" + IL_000e: stloc.1 + IL_000f: ldloc.1 + IL_0010: ldloc.0 + IL_0011: call "void System.Runtime.InteropServices.CollectionsMarshal.SetCount(System.Collections.Generic.List, int)" + IL_0016: ldloc.1 + IL_0017: call "System.Span System.Runtime.InteropServices.CollectionsMarshal.AsSpan(System.Collections.Generic.List)" + IL_001c: stloc.2 + IL_001d: ldc.i4.0 + IL_001e: stloc.3 + IL_001f: callvirt "MyCollection.Enumerator MyCollection.GetEnumerator()" + IL_0024: stloc.s V_4 + .try + { + IL_0026: br.s IL_0040 + IL_0028: ldloca.s V_4 + IL_002a: call "int MyCollection.Enumerator.Current.get" + IL_002f: stloc.s V_5 + IL_0031: ldloca.s V_2 + IL_0033: ldloc.3 + IL_0034: call "ref int System.Span.this[int].get" + IL_0039: ldloc.s V_5 + IL_003b: stind.i4 + IL_003c: ldloc.3 + IL_003d: ldc.i4.1 + IL_003e: add + IL_003f: stloc.3 + IL_0040: ldloca.s V_4 + IL_0042: call "bool MyCollection.Enumerator.MoveNext()" + IL_0047: brtrue.s IL_0028 + IL_0049: leave.s IL_0059 + } + finally + { + IL_004b: ldloca.s V_4 + IL_004d: constrained. "MyCollection.Enumerator" + IL_0053: callvirt "void System.IDisposable.Dispose()" + IL_0058: endfinally + } + IL_0059: ldloc.1 + IL_005a: ret + } + """); + break; + case TargetFramework.Standard: + verifier.VerifyIL("C.M", """ + { + // Code size 21 (0x15) + .maxstack 3 + .locals init (MyCollection V_0) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: callvirt "int MyCollection.Count.get" + IL_0008: newobj "System.Collections.Generic.List..ctor(int)" + IL_000d: dup + IL_000e: ldloc.0 + IL_000f: callvirt "void System.Collections.Generic.List.AddRange(System.Collections.Generic.IEnumerable)" + IL_0014: ret + } + """); + break; + } } [Fact] From ea78e18dc58c4d7edd57dc14c842441bad06878a Mon Sep 17 00:00:00 2001 From: DoctorKrolic Date: Wed, 14 Aug 2024 22:20:29 +0300 Subject: [PATCH 7/8] Conditionaly include output --- .../Semantics/CollectionExpressionTests.cs | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs index 2263f07958d30..cd1755357398f 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs @@ -34722,7 +34722,18 @@ static void Main() } """; - var verifier = CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3],", targetFramework: targetFramework, verify: Verification.Skipped); + var expectedOutputString = "[1, 2, 3],"; + var verifier = CompileAndVerify( + [source, s_collectionExtensions], + expectedOutput: targetFramework switch + { + TargetFramework.Net80 => IncludeExpectedOutput(expectedOutputString), + TargetFramework.Standard => expectedOutputString, + _ => throw new InvalidOperationException("Update expected output!"), + }, + targetFramework: targetFramework, + verify: Verification.Skipped); + verifier.VerifyDiagnostics(); switch (targetFramework) @@ -34806,6 +34817,8 @@ .locals init (MyCollection V_0) } """); break; + default: + throw new InvalidOperationException("Update verified IL!"); } } @@ -35117,7 +35130,18 @@ static void Main() } """; - var verifier = CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3],", targetFramework: targetFramework, verify: Verification.Skipped); + var expectedOutputString = "[1, 2, 3],"; + var verifier = CompileAndVerify( + [source, s_collectionExtensions], + expectedOutput: targetFramework switch + { + TargetFramework.Net80 => IncludeExpectedOutput(expectedOutputString), + TargetFramework.Standard => expectedOutputString, + _ => throw new InvalidOperationException("Update expected output!"), + }, + targetFramework: targetFramework, + verify: Verification.Skipped); + verifier.VerifyDiagnostics(); switch (targetFramework) @@ -35200,6 +35224,8 @@ .locals init (MyCollection V_0) } """); break; + default: + throw new InvalidOperationException("Update verified IL!"); } } From bb56e4e6393f3ccadd4508dbb257cc281793a52d Mon Sep 17 00:00:00 2001 From: DoctorKrolic Date: Wed, 14 Aug 2024 22:37:04 +0300 Subject: [PATCH 8/8] Simplify --- .../Semantics/CollectionExpressionTests.cs | 332 ++++++++---------- 1 file changed, 153 insertions(+), 179 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs index cd1755357398f..74bdbee7ac5d1 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs @@ -34725,101 +34725,88 @@ static void Main() var expectedOutputString = "[1, 2, 3],"; var verifier = CompileAndVerify( [source, s_collectionExtensions], - expectedOutput: targetFramework switch - { - TargetFramework.Net80 => IncludeExpectedOutput(expectedOutputString), - TargetFramework.Standard => expectedOutputString, - _ => throw new InvalidOperationException("Update expected output!"), - }, + expectedOutput: targetFramework == TargetFramework.Standard ? expectedOutputString : IncludeExpectedOutput(expectedOutputString), targetFramework: targetFramework, verify: Verification.Skipped); verifier.VerifyDiagnostics(); - switch (targetFramework) - { - case TargetFramework.Net80: - verifier.VerifyIL("C.M", """ - { - // Code size 89 (0x59) - .maxstack 3 - .locals init (int V_0, - System.Collections.Generic.List V_1, - System.Span V_2, - int V_3, - System.Collections.Generic.IEnumerator V_4, - int V_5) - IL_0000: ldarg.0 - IL_0001: dup - IL_0002: callvirt "int MyCollection.Count.get" - IL_0007: stloc.0 - IL_0008: ldloc.0 - IL_0009: newobj "System.Collections.Generic.List..ctor(int)" - IL_000e: stloc.1 - IL_000f: ldloc.1 - IL_0010: ldloc.0 - IL_0011: call "void System.Runtime.InteropServices.CollectionsMarshal.SetCount(System.Collections.Generic.List, int)" - IL_0016: ldloc.1 - IL_0017: call "System.Span System.Runtime.InteropServices.CollectionsMarshal.AsSpan(System.Collections.Generic.List)" - IL_001c: stloc.2 - IL_001d: ldc.i4.0 - IL_001e: stloc.3 - IL_001f: callvirt "System.Collections.Generic.IEnumerator MyCollection.GetEnumerator()" - IL_0024: stloc.s V_4 - .try - { - IL_0026: br.s IL_0040 - IL_0028: ldloc.s V_4 - IL_002a: callvirt "int System.Collections.Generic.IEnumerator.Current.get" - IL_002f: stloc.s V_5 - IL_0031: ldloca.s V_2 - IL_0033: ldloc.3 - IL_0034: call "ref int System.Span.this[int].get" - IL_0039: ldloc.s V_5 - IL_003b: stind.i4 - IL_003c: ldloc.3 - IL_003d: ldc.i4.1 - IL_003e: add - IL_003f: stloc.3 - IL_0040: ldloc.s V_4 - IL_0042: callvirt "bool System.Collections.IEnumerator.MoveNext()" - IL_0047: brtrue.s IL_0028 - IL_0049: leave.s IL_0057 - } - finally - { - IL_004b: ldloc.s V_4 - IL_004d: brfalse.s IL_0056 - IL_004f: ldloc.s V_4 - IL_0051: callvirt "void System.IDisposable.Dispose()" - IL_0056: endfinally - } - IL_0057: ldloc.1 - IL_0058: ret - } - """); - break; - case TargetFramework.Standard: - verifier.VerifyIL("C.M", """ - { - // Code size 21 (0x15) - .maxstack 3 - .locals init (MyCollection V_0) - IL_0000: ldarg.0 - IL_0001: stloc.0 - IL_0002: ldloc.0 - IL_0003: callvirt "int MyCollection.Count.get" - IL_0008: newobj "System.Collections.Generic.List..ctor(int)" - IL_000d: dup - IL_000e: ldloc.0 - IL_000f: callvirt "void System.Collections.Generic.List.AddRange(System.Collections.Generic.IEnumerable)" - IL_0014: ret - } - """); - break; - default: - throw new InvalidOperationException("Update verified IL!"); - } + var expectedIL = targetFramework == TargetFramework.Standard ? """ + { + // Code size 21 (0x15) + .maxstack 3 + .locals init (MyCollection V_0) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: callvirt "int MyCollection.Count.get" + IL_0008: newobj "System.Collections.Generic.List..ctor(int)" + IL_000d: dup + IL_000e: ldloc.0 + IL_000f: callvirt "void System.Collections.Generic.List.AddRange(System.Collections.Generic.IEnumerable)" + IL_0014: ret + } + """ : """ + { + // Code size 89 (0x59) + .maxstack 3 + .locals init (int V_0, + System.Collections.Generic.List V_1, + System.Span V_2, + int V_3, + System.Collections.Generic.IEnumerator V_4, + int V_5) + IL_0000: ldarg.0 + IL_0001: dup + IL_0002: callvirt "int MyCollection.Count.get" + IL_0007: stloc.0 + IL_0008: ldloc.0 + IL_0009: newobj "System.Collections.Generic.List..ctor(int)" + IL_000e: stloc.1 + IL_000f: ldloc.1 + IL_0010: ldloc.0 + IL_0011: call "void System.Runtime.InteropServices.CollectionsMarshal.SetCount(System.Collections.Generic.List, int)" + IL_0016: ldloc.1 + IL_0017: call "System.Span System.Runtime.InteropServices.CollectionsMarshal.AsSpan(System.Collections.Generic.List)" + IL_001c: stloc.2 + IL_001d: ldc.i4.0 + IL_001e: stloc.3 + IL_001f: callvirt "System.Collections.Generic.IEnumerator MyCollection.GetEnumerator()" + IL_0024: stloc.s V_4 + .try + { + IL_0026: br.s IL_0040 + IL_0028: ldloc.s V_4 + IL_002a: callvirt "int System.Collections.Generic.IEnumerator.Current.get" + IL_002f: stloc.s V_5 + IL_0031: ldloca.s V_2 + IL_0033: ldloc.3 + IL_0034: call "ref int System.Span.this[int].get" + IL_0039: ldloc.s V_5 + IL_003b: stind.i4 + IL_003c: ldloc.3 + IL_003d: ldc.i4.1 + IL_003e: add + IL_003f: stloc.3 + IL_0040: ldloc.s V_4 + IL_0042: callvirt "bool System.Collections.IEnumerator.MoveNext()" + IL_0047: brtrue.s IL_0028 + IL_0049: leave.s IL_0057 + } + finally + { + IL_004b: ldloc.s V_4 + IL_004d: brfalse.s IL_0056 + IL_004f: ldloc.s V_4 + IL_0051: callvirt "void System.IDisposable.Dispose()" + IL_0056: endfinally + } + IL_0057: ldloc.1 + IL_0058: ret + } + """; + + verifier.VerifyIL("C.M", expectedIL); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74615")] @@ -35133,100 +35120,87 @@ static void Main() var expectedOutputString = "[1, 2, 3],"; var verifier = CompileAndVerify( [source, s_collectionExtensions], - expectedOutput: targetFramework switch - { - TargetFramework.Net80 => IncludeExpectedOutput(expectedOutputString), - TargetFramework.Standard => expectedOutputString, - _ => throw new InvalidOperationException("Update expected output!"), - }, + expectedOutput: targetFramework == TargetFramework.Standard ? expectedOutputString : IncludeExpectedOutput(expectedOutputString), targetFramework: targetFramework, verify: Verification.Skipped); verifier.VerifyDiagnostics(); - switch (targetFramework) - { - case TargetFramework.Net80: - verifier.VerifyIL("C.M", """ - { - // Code size 91 (0x5b) - .maxstack 3 - .locals init (int V_0, - System.Collections.Generic.List V_1, - System.Span V_2, - int V_3, - MyCollection.Enumerator V_4, - int V_5) - IL_0000: ldarg.0 - IL_0001: dup - IL_0002: callvirt "int MyCollection.Count.get" - IL_0007: stloc.0 - IL_0008: ldloc.0 - IL_0009: newobj "System.Collections.Generic.List..ctor(int)" - IL_000e: stloc.1 - IL_000f: ldloc.1 - IL_0010: ldloc.0 - IL_0011: call "void System.Runtime.InteropServices.CollectionsMarshal.SetCount(System.Collections.Generic.List, int)" - IL_0016: ldloc.1 - IL_0017: call "System.Span System.Runtime.InteropServices.CollectionsMarshal.AsSpan(System.Collections.Generic.List)" - IL_001c: stloc.2 - IL_001d: ldc.i4.0 - IL_001e: stloc.3 - IL_001f: callvirt "MyCollection.Enumerator MyCollection.GetEnumerator()" - IL_0024: stloc.s V_4 - .try - { - IL_0026: br.s IL_0040 - IL_0028: ldloca.s V_4 - IL_002a: call "int MyCollection.Enumerator.Current.get" - IL_002f: stloc.s V_5 - IL_0031: ldloca.s V_2 - IL_0033: ldloc.3 - IL_0034: call "ref int System.Span.this[int].get" - IL_0039: ldloc.s V_5 - IL_003b: stind.i4 - IL_003c: ldloc.3 - IL_003d: ldc.i4.1 - IL_003e: add - IL_003f: stloc.3 - IL_0040: ldloca.s V_4 - IL_0042: call "bool MyCollection.Enumerator.MoveNext()" - IL_0047: brtrue.s IL_0028 - IL_0049: leave.s IL_0059 - } - finally - { - IL_004b: ldloca.s V_4 - IL_004d: constrained. "MyCollection.Enumerator" - IL_0053: callvirt "void System.IDisposable.Dispose()" - IL_0058: endfinally - } - IL_0059: ldloc.1 - IL_005a: ret - } - """); - break; - case TargetFramework.Standard: - verifier.VerifyIL("C.M", """ - { - // Code size 21 (0x15) - .maxstack 3 - .locals init (MyCollection V_0) - IL_0000: ldarg.0 - IL_0001: stloc.0 - IL_0002: ldloc.0 - IL_0003: callvirt "int MyCollection.Count.get" - IL_0008: newobj "System.Collections.Generic.List..ctor(int)" - IL_000d: dup - IL_000e: ldloc.0 - IL_000f: callvirt "void System.Collections.Generic.List.AddRange(System.Collections.Generic.IEnumerable)" - IL_0014: ret - } - """); - break; - default: - throw new InvalidOperationException("Update verified IL!"); - } + var expectedIL = targetFramework == TargetFramework.Standard ? """ + { + // Code size 21 (0x15) + .maxstack 3 + .locals init (MyCollection V_0) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: callvirt "int MyCollection.Count.get" + IL_0008: newobj "System.Collections.Generic.List..ctor(int)" + IL_000d: dup + IL_000e: ldloc.0 + IL_000f: callvirt "void System.Collections.Generic.List.AddRange(System.Collections.Generic.IEnumerable)" + IL_0014: ret + } + """ : """ + { + // Code size 91 (0x5b) + .maxstack 3 + .locals init (int V_0, + System.Collections.Generic.List V_1, + System.Span V_2, + int V_3, + MyCollection.Enumerator V_4, + int V_5) + IL_0000: ldarg.0 + IL_0001: dup + IL_0002: callvirt "int MyCollection.Count.get" + IL_0007: stloc.0 + IL_0008: ldloc.0 + IL_0009: newobj "System.Collections.Generic.List..ctor(int)" + IL_000e: stloc.1 + IL_000f: ldloc.1 + IL_0010: ldloc.0 + IL_0011: call "void System.Runtime.InteropServices.CollectionsMarshal.SetCount(System.Collections.Generic.List, int)" + IL_0016: ldloc.1 + IL_0017: call "System.Span System.Runtime.InteropServices.CollectionsMarshal.AsSpan(System.Collections.Generic.List)" + IL_001c: stloc.2 + IL_001d: ldc.i4.0 + IL_001e: stloc.3 + IL_001f: callvirt "MyCollection.Enumerator MyCollection.GetEnumerator()" + IL_0024: stloc.s V_4 + .try + { + IL_0026: br.s IL_0040 + IL_0028: ldloca.s V_4 + IL_002a: call "int MyCollection.Enumerator.Current.get" + IL_002f: stloc.s V_5 + IL_0031: ldloca.s V_2 + IL_0033: ldloc.3 + IL_0034: call "ref int System.Span.this[int].get" + IL_0039: ldloc.s V_5 + IL_003b: stind.i4 + IL_003c: ldloc.3 + IL_003d: ldc.i4.1 + IL_003e: add + IL_003f: stloc.3 + IL_0040: ldloca.s V_4 + IL_0042: call "bool MyCollection.Enumerator.MoveNext()" + IL_0047: brtrue.s IL_0028 + IL_0049: leave.s IL_0059 + } + finally + { + IL_004b: ldloca.s V_4 + IL_004d: constrained. "MyCollection.Enumerator" + IL_0053: callvirt "void System.IDisposable.Dispose()" + IL_0058: endfinally + } + IL_0059: ldloc.1 + IL_005a: ret + } + """; + + verifier.VerifyIL("C.M", expectedIL); } [Fact]