From be65da03ac2d6c5f4bf553c31081a91abc468d9a Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Fri, 19 Jan 2024 13:18:45 -0800 Subject: [PATCH 1/3] Prefer converting to ReadOnlySpan for spread optimizatio when we do not need to write --- .../LocalRewriter_CollectionExpression.cs | 34 +-- .../Semantics/CollectionExpressionTests.cs | 268 ++++++++++++++---- 2 files changed, 228 insertions(+), 74 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs index 1bc4a5f770fd1..43e94d7c2c6e5 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs @@ -538,11 +538,11 @@ private BoundExpression CreateAndPopulateArray(BoundCollectionExpression node, A return _factory.Call(rewrittenSpreadExpression, listToArrayMethod.AsMember((NamedTypeSymbol)spreadExpression.Type!)); } - if (TryGetSpanConversion(spreadExpression.Type, out var asSpanMethod)) + if (TryGetSpanConversion(spreadExpression.Type, writableOnly: false, out var asSpanMethod)) { var spanType = CallAsSpanMethod(spreadExpression, asSpanMethod).Type!.OriginalDefinition; - if (tryGetToArrayMethod(spanType, WellKnownType.System_Span_T, WellKnownMember.System_Span_T__ToArray, out var toArrayMethod) - || tryGetToArrayMethod(spanType, WellKnownType.System_ReadOnlySpan_T, WellKnownMember.System_ReadOnlySpan_T__ToArray, out toArrayMethod)) + if (tryGetToArrayMethod(spanType, WellKnownType.System_ReadOnlySpan_T, WellKnownMember.System_ReadOnlySpan_T__ToArray, out var toArrayMethod) + || tryGetToArrayMethod(spanType, WellKnownType.System_Span_T, WellKnownMember.System_Span_T__ToArray, out toArrayMethod)) { var rewrittenSpreadExpression = CallAsSpanMethod(VisitExpression(spreadExpression), asSpanMethod); return _factory.Call(rewrittenSpreadExpression, toArrayMethod.AsMember((NamedTypeSymbol)rewrittenSpreadExpression.Type!)); @@ -649,7 +649,7 @@ bool tryGetToArrayMethod(TypeSymbol spreadTypeOriginalDefinition, WellKnownType // https://github.com/dotnet/roslyn/issues/71270 // Could save the targetSpan to temp in the enclosing scope, but need to make sure we are async-safe etc. - if (!TryConvertToSpanOrReadOnlySpan(arrayTemp, out var targetSpan)) + if (!TryConvertToSpan(arrayTemp, writableOnly: true, out var targetSpan)) return false; PerformCopyToOptimization(sideEffects, localsBuilder, indexTemp, targetSpan, rewrittenSpreadOperand, spanSliceMethod, spreadElementAsSpan, getLengthMethod, copyToMethod); @@ -667,15 +667,16 @@ bool tryGetToArrayMethod(TypeSymbol spreadTypeOriginalDefinition, WellKnownType arrayType); } - /// - /// Returns true if type is convertible to Span or ReadOnlySpan. - /// If non-identity conversion, also returns a non-null asSpanMethod. - /// + /// + /// If is , will only return true with a conversion to writable Span. + /// Otherwise, will return a conversion directly to ReadOnlySpan if one is known, and may return a conversion to Span if no direct conversion to ReadOnlySpan is known, or if the type is already a Span. + /// If non-identity conversion, also returns a non-null . + /// /// We are assuming that the well-known types we are converting to/from do not have constraints on their type parameters. - private bool TryGetSpanConversion(TypeSymbol type, out MethodSymbol? asSpanMethod) + private bool TryGetSpanConversion(TypeSymbol type, bool writableOnly, out MethodSymbol? asSpanMethod) { if (type is ArrayTypeSymbol { IsSZArray: true } arrayType - && _factory.WellKnownMethod(WellKnownMember.System_Span_T__ctor_Array, isOptional: true) is { } spanCtorArray) + && _factory.WellKnownMethod(writableOnly ? WellKnownMember.System_Span_T__ctor_Array : WellKnownMember.System_ReadOnlySpan_T__ctor_Array, isOptional: true) is { } spanCtorArray) { // conversion to 'object' will fail if, for example, 'arrayType.ElementType' is a pointer. var useSiteInfo = CompoundUseSiteInfo.Discarded; @@ -692,14 +693,15 @@ private bool TryGetSpanConversion(TypeSymbol type, out MethodSymbol? asSpanMetho return false; } - if (namedType.OriginalDefinition.Equals(_compilation.GetWellKnownType(WellKnownType.System_Span_T), TypeCompareKind.ConsiderEverything) - || namedType.OriginalDefinition.Equals(_compilation.GetWellKnownType(WellKnownType.System_ReadOnlySpan_T), TypeCompareKind.ConsiderEverything)) + if ((!writableOnly && namedType.OriginalDefinition.Equals(_compilation.GetWellKnownType(WellKnownType.System_ReadOnlySpan_T), TypeCompareKind.ConsiderEverything)) + || namedType.OriginalDefinition.Equals(_compilation.GetWellKnownType(WellKnownType.System_Span_T), TypeCompareKind.ConsiderEverything)) { asSpanMethod = null; return true; } - if (namedType.OriginalDefinition.Equals(_compilation.GetWellKnownType(WellKnownType.System_Collections_Immutable_ImmutableArray_T), TypeCompareKind.ConsiderEverything) + if (!writableOnly + && namedType.OriginalDefinition.Equals(_compilation.GetWellKnownType(WellKnownType.System_Collections_Immutable_ImmutableArray_T), TypeCompareKind.ConsiderEverything) && _factory.WellKnownMethod(WellKnownMember.System_Collections_Immutable_ImmutableArray_T__AsSpan, isOptional: true) is { } immutableArrayAsSpanMethod) { asSpanMethod = immutableArrayAsSpanMethod.AsMember(namedType); @@ -717,12 +719,12 @@ private bool TryGetSpanConversion(TypeSymbol type, out MethodSymbol? asSpanMetho return false; } - private bool TryConvertToSpanOrReadOnlySpan(BoundExpression expression, [NotNullWhen(true)] out BoundExpression? span) + private bool TryConvertToSpan(BoundExpression expression, bool writableOnly, [NotNullWhen(true)] out BoundExpression? span) { var type = expression.Type; Debug.Assert(type is not null); - if (!TryGetSpanConversion(type, out var asSpanMethod)) + if (!TryGetSpanConversion(type, writableOnly, out var asSpanMethod)) { span = null; return false; @@ -768,7 +770,7 @@ private BoundExpression CallAsSpanMethod(BoundExpression spreadExpression, Metho if (_factory.WellKnownMethod(WellKnownMember.System_Span_T__Slice_Int_Int, isOptional: true) is not { } spanSliceMethod) return null; - if (!TryConvertToSpanOrReadOnlySpan(rewrittenSpreadOperand, out var spreadOperandAsSpan)) + if (!TryConvertToSpan(rewrittenSpreadOperand, writableOnly: false, out var spreadOperandAsSpan)) return null; if ((getSpanMethodsForSpread(WellKnownType.System_ReadOnlySpan_T, WellKnownMember.System_ReadOnlySpan_T__get_Length, WellKnownMember.System_ReadOnlySpan_T__CopyTo_Span_T) diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs index 625a7aa4f1b3e..27dbd552a9b77 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs @@ -7187,12 +7187,12 @@ .maxstack 3 { // Code size 21 (0x15) .maxstack 2 - .locals init (System.Span V_0) + .locals init (System.ReadOnlySpan V_0) IL_0000: ldarg.0 - IL_0001: newobj "System.Span..ctor(int[])" + IL_0001: newobj "System.ReadOnlySpan..ctor(int[])" IL_0006: stloc.0 IL_0007: ldloca.s V_0 - IL_0009: call "int[] System.Span.ToArray()" + IL_0009: call "int[] System.ReadOnlySpan.ToArray()" IL_000e: ldc.i4.0 IL_000f: call "void CollectionExtensions.Report(object, bool)" IL_0014: ret @@ -8419,7 +8419,7 @@ .maxstack 5 .locals init (object[] V_0, System.Span V_1, int V_2, - System.Span V_3) + System.ReadOnlySpan V_3) IL_0000: ldc.i4.3 IL_0001: newarr "object" IL_0006: dup @@ -8454,17 +8454,17 @@ .locals init (object[] V_0, IL_003f: stloc.2 IL_0040: ldloca.s V_3 IL_0042: ldloc.0 - IL_0043: call "System.Span..ctor(object[])" + IL_0043: call "System.ReadOnlySpan..ctor(object[])" IL_0048: ldloca.s V_3 IL_004a: ldloca.s V_1 IL_004c: ldloc.2 IL_004d: ldloca.s V_3 - IL_004f: call "int System.Span.Length.get" + IL_004f: call "int System.ReadOnlySpan.Length.get" IL_0054: call "System.Span System.Span.Slice(int, int)" - IL_0059: call "void System.Span.CopyTo(System.Span)" + IL_0059: call "void System.ReadOnlySpan.CopyTo(System.Span)" IL_005e: ldloc.2 IL_005f: ldloca.s V_3 - IL_0061: call "int System.Span.Length.get" + IL_0061: call "int System.ReadOnlySpan.Length.get" IL_0066: add IL_0067: stloc.2 IL_0068: ldc.i4.0 @@ -8616,7 +8616,7 @@ .maxstack 5 .locals init (object[] V_0, System.Span V_1, int V_2, - System.Span V_3) + System.ReadOnlySpan V_3) IL_0000: ldc.i4.3 IL_0001: newarr "object" IL_0006: dup @@ -8651,17 +8651,17 @@ .locals init (object[] V_0, IL_003f: stloc.2 IL_0040: ldloca.s V_3 IL_0042: ldloc.0 - IL_0043: call "System.Span..ctor(object[])" + IL_0043: call "System.ReadOnlySpan..ctor(object[])" IL_0048: ldloca.s V_3 IL_004a: ldloca.s V_1 IL_004c: ldloc.2 IL_004d: ldloca.s V_3 - IL_004f: call "int System.Span.Length.get" + IL_004f: call "int System.ReadOnlySpan.Length.get" IL_0054: call "System.Span System.Span.Slice(int, int)" - IL_0059: call "void System.Span.CopyTo(System.Span)" + IL_0059: call "void System.ReadOnlySpan.CopyTo(System.Span)" IL_005e: ldloc.2 IL_005f: ldloca.s V_3 - IL_0061: call "int System.Span.Length.get" + IL_0061: call "int System.ReadOnlySpan.Length.get" IL_0066: add IL_0067: stloc.2 IL_0068: ldc.i4.0 @@ -8883,7 +8883,7 @@ .locals init (int[] V_0, System.Collections.Generic.List V_2, int V_3, int[] V_4, - System.Span V_5, + System.ReadOnlySpan V_5, System.Span V_6, System.Span V_7, int[,] V_8, @@ -8939,7 +8939,7 @@ .locals init (int[] V_0, IL_006c: stloc.s V_4 IL_006e: ldloca.s V_5 IL_0070: ldloc.0 - IL_0071: call "System.Span..ctor(int[])" + IL_0071: call "System.ReadOnlySpan..ctor(int[])" IL_0076: ldloca.s V_5 IL_0078: ldloc.s V_4 IL_007a: newobj "System.Span..ctor(int[])" @@ -8947,12 +8947,12 @@ .locals init (int[] V_0, IL_0081: ldloca.s V_7 IL_0083: ldloc.3 IL_0084: ldloca.s V_5 - IL_0086: call "int System.Span.Length.get" + IL_0086: call "int System.ReadOnlySpan.Length.get" IL_008b: call "System.Span System.Span.Slice(int, int)" - IL_0090: call "void System.Span.CopyTo(System.Span)" + IL_0090: call "void System.ReadOnlySpan.CopyTo(System.Span)" IL_0095: ldloc.3 IL_0096: ldloca.s V_5 - IL_0098: call "int System.Span.Length.get" + IL_0098: call "int System.ReadOnlySpan.Length.get" IL_009d: add IL_009e: stloc.3 IL_009f: ldloc.1 @@ -17765,7 +17765,7 @@ .maxstack 5 .locals init (T[] V_0, System.Span V_1, int V_2, - System.Span V_3) + System.ReadOnlySpan V_3) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: newobj "System.Collections.Generic.List..ctor()" @@ -17781,17 +17781,17 @@ .locals init (T[] V_0, IL_0018: stloc.2 IL_0019: ldloca.s V_3 IL_001b: ldloc.0 - IL_001c: call "System.Span..ctor(T[])" + IL_001c: call "System.ReadOnlySpan..ctor(T[])" IL_0021: ldloca.s V_3 IL_0023: ldloca.s V_1 IL_0025: ldloc.2 IL_0026: ldloca.s V_3 - IL_0028: call "int System.Span.Length.get" + IL_0028: call "int System.ReadOnlySpan.Length.get" IL_002d: call "System.Span System.Span.Slice(int, int)" - IL_0032: call "void System.Span.CopyTo(System.Span)" + IL_0032: call "void System.ReadOnlySpan.CopyTo(System.Span)" IL_0037: ldloc.2 IL_0038: ldloca.s V_3 - IL_003a: call "int System.Span.Length.get" + IL_003a: call "int System.ReadOnlySpan.Length.get" IL_003f: add IL_0040: stloc.2 IL_0041: ret @@ -20188,13 +20188,13 @@ .locals init (<>y__InlineArray3 V_0) { // Code size 26 (0x1a) .maxstack 2 - .locals init (System.Span V_0) + .locals init (System.ReadOnlySpan V_0) IL_0000: ldarg.0 IL_0001: ldarg.2 - IL_0002: newobj "System.Span..ctor(T[])" + IL_0002: newobj "System.ReadOnlySpan..ctor(T[])" IL_0007: stloc.0 IL_0008: ldloca.s V_0 - IL_000a: call "T[] System.Span.ToArray()" + IL_000a: call "T[] System.ReadOnlySpan.ToArray()" IL_000f: newobj "System.Span..ctor(T[])" IL_0014: call "R..ctor(scoped System.Span)" IL_0019: ret @@ -21030,17 +21030,17 @@ static void M(bool b, T[] a) // Code size 40 (0x28) .maxstack 2 .locals init (System.Span V_0, //s - System.Span V_1) + System.ReadOnlySpan V_1) IL_0000: ldloca.s V_0 IL_0002: initobj "System.Span" IL_0008: ldarg.0 IL_0009: brfalse.s IL_0020 IL_000b: ldloca.s V_0 IL_000d: ldarg.1 - IL_000e: newobj "System.Span..ctor(T[])" + IL_000e: newobj "System.ReadOnlySpan..ctor(T[])" IL_0013: stloc.1 IL_0014: ldloca.s V_1 - IL_0016: call "T[] System.Span.ToArray()" + IL_0016: call "T[] System.ReadOnlySpan.ToArray()" IL_001b: call "System.Span..ctor(T[])" IL_0020: ldloca.s V_0 IL_0022: call "void CollectionExtensions.Report(in System.Span)" @@ -25963,16 +25963,16 @@ static void Main() { // Code size 47 (0x2f) .maxstack 3 - .locals init (System.Span V_0) + .locals init (System.ReadOnlySpan V_0) IL_0000: ldc.i4.3 IL_0001: newarr "int" IL_0006: dup IL_0007: ldtoken ".__StaticArrayInitTypeSize=12 .4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D" IL_000c: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" - IL_0011: newobj "System.Span..ctor(int[])" + IL_0011: newobj "System.ReadOnlySpan..ctor(int[])" IL_0016: stloc.0 IL_0017: ldloca.s V_0 - IL_0019: call "int[] System.Span.ToArray()" + IL_0019: call "int[] System.ReadOnlySpan.ToArray()" IL_001e: call "System.Collections.Immutable.ImmutableArray System.Runtime.InteropServices.ImmutableCollectionsMarshal.AsImmutableArray(int[])" IL_0023: box "System.Collections.Immutable.ImmutableArray" IL_0028: ldc.i4.0 @@ -29595,8 +29595,8 @@ .locals init (int[] V_0, int[] V_1, System.Span V_2, int V_3, - System.Span V_4, - System.Span V_5) + System.ReadOnlySpan V_4, + System.ReadOnlySpan V_5) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldarg.1 @@ -29618,32 +29618,32 @@ .locals init (int[] V_0, IL_001e: stloc.3 IL_001f: ldloca.s V_4 IL_0021: ldloc.0 - IL_0022: call "System.Span..ctor(int[])" + IL_0022: call "System.ReadOnlySpan..ctor(int[])" IL_0027: ldloca.s V_4 IL_0029: ldloca.s V_2 IL_002b: ldloc.3 IL_002c: ldloca.s V_4 - IL_002e: call "int System.Span.Length.get" + IL_002e: call "int System.ReadOnlySpan.Length.get" IL_0033: call "System.Span System.Span.Slice(int, int)" - IL_0038: call "void System.Span.CopyTo(System.Span)" + IL_0038: call "void System.ReadOnlySpan.CopyTo(System.Span)" IL_003d: ldloc.3 IL_003e: ldloca.s V_4 - IL_0040: call "int System.Span.Length.get" + IL_0040: call "int System.ReadOnlySpan.Length.get" IL_0045: add IL_0046: stloc.3 IL_0047: ldloca.s V_5 IL_0049: ldloc.1 - IL_004a: call "System.Span..ctor(int[])" + IL_004a: call "System.ReadOnlySpan..ctor(int[])" IL_004f: ldloca.s V_5 IL_0051: ldloca.s V_2 IL_0053: ldloc.3 IL_0054: ldloca.s V_5 - IL_0056: call "int System.Span.Length.get" + IL_0056: call "int System.ReadOnlySpan.Length.get" IL_005b: call "System.Span System.Span.Slice(int, int)" - IL_0060: call "void System.Span.CopyTo(System.Span)" + IL_0060: call "void System.ReadOnlySpan.CopyTo(System.Span)" IL_0065: ldloc.3 IL_0066: ldloca.s V_5 - IL_0068: call "int System.Span.Length.get" + IL_0068: call "int System.ReadOnlySpan.Length.get" IL_006d: add IL_006e: stloc.3 IL_006f: ldc.i4.0 @@ -29653,6 +29653,158 @@ .locals init (int[] V_0, """); } + [Fact] + public void ArrayToArray_Covariant_SingleSpread() + { + var source = """ + class Base { } + class Derived : Base { } + + class C + { + static void Main() + { + Base[] array = new Derived[] { new Derived() }; + array.Report(); + + Base[] copy = [..array]; + copy.Report(); + } + } + """; + + var verifier = CompileAndVerify(new[] { source, s_collectionExtensionsWithSpan }, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("[Derived], [Derived], "), targetFramework: TargetFramework.Net80); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.Main", """ + { + // Code size 43 (0x2b) + .maxstack 4 + .locals init (Base[] V_0, + System.ReadOnlySpan V_1) + IL_0000: ldc.i4.1 + IL_0001: newarr "Derived" + IL_0006: dup + IL_0007: ldc.i4.0 + IL_0008: newobj "Derived..ctor()" + IL_000d: stelem.ref + IL_000e: stloc.0 + IL_000f: ldloc.0 + IL_0010: dup + IL_0011: ldc.i4.0 + IL_0012: call "void CollectionExtensions.Report(object, bool)" + IL_0017: newobj "System.ReadOnlySpan..ctor(Base[])" + IL_001c: stloc.1 + IL_001d: ldloca.s V_1 + IL_001f: call "Base[] System.ReadOnlySpan.ToArray()" + IL_0024: ldc.i4.0 + IL_0025: call "void CollectionExtensions.Report(object, bool)" + IL_002a: ret + } + """); + } + + [Fact] + public void ArrayToArray_Covariant_MultipleSpreads() + { + var source = """ + class Base { } + class Derived : Base { } + + class C + { + static void Main() + { + Base[] array = new Derived[] { new Derived() }; + array.Report(); + + Base[] copy = [..array, ..array]; + copy.Report(); + } + } + """; + + var verifier = CompileAndVerify(new[] { source, s_collectionExtensionsWithSpan }, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("[Derived], [Derived, Derived],"), targetFramework: TargetFramework.Net80); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.Main", """ + { + // Code size 145 (0x91) + .maxstack 4 + .locals init (Base[] V_0, + Base[] V_1, + int V_2, + Base[] V_3, + System.ReadOnlySpan V_4, + System.ReadOnlySpan V_5, + System.Span V_6) + IL_0000: ldc.i4.1 + IL_0001: newarr "Derived" + IL_0006: dup + IL_0007: ldc.i4.0 + IL_0008: newobj "Derived..ctor()" + IL_000d: stelem.ref + IL_000e: stloc.0 + IL_000f: ldloc.0 + IL_0010: dup + IL_0011: ldc.i4.0 + IL_0012: call "void CollectionExtensions.Report(object, bool)" + IL_0017: dup + IL_0018: stloc.0 + IL_0019: stloc.1 + IL_001a: ldc.i4.0 + IL_001b: stloc.2 + IL_001c: ldloc.0 + IL_001d: ldlen + IL_001e: conv.i4 + IL_001f: ldloc.1 + IL_0020: ldlen + IL_0021: conv.i4 + IL_0022: add + IL_0023: newarr "Base" + IL_0028: stloc.3 + IL_0029: ldloca.s V_4 + IL_002b: ldloc.0 + IL_002c: call "System.ReadOnlySpan..ctor(Base[])" + IL_0031: ldloca.s V_4 + IL_0033: ldloc.3 + IL_0034: newobj "System.Span..ctor(Base[])" + IL_0039: stloc.s V_6 + IL_003b: ldloca.s V_6 + IL_003d: ldloc.2 + IL_003e: ldloca.s V_4 + IL_0040: call "int System.ReadOnlySpan.Length.get" + IL_0045: call "System.Span System.Span.Slice(int, int)" + IL_004a: call "void System.ReadOnlySpan.CopyTo(System.Span)" + IL_004f: ldloc.2 + IL_0050: ldloca.s V_4 + IL_0052: call "int System.ReadOnlySpan.Length.get" + IL_0057: add + IL_0058: stloc.2 + IL_0059: ldloca.s V_5 + IL_005b: ldloc.1 + IL_005c: call "System.ReadOnlySpan..ctor(Base[])" + IL_0061: ldloca.s V_5 + IL_0063: ldloc.3 + IL_0064: newobj "System.Span..ctor(Base[])" + IL_0069: stloc.s V_6 + IL_006b: ldloca.s V_6 + IL_006d: ldloc.2 + IL_006e: ldloca.s V_5 + IL_0070: call "int System.ReadOnlySpan.Length.get" + IL_0075: call "System.Span System.Span.Slice(int, int)" + IL_007a: call "void System.ReadOnlySpan.CopyTo(System.Span)" + IL_007f: ldloc.2 + IL_0080: ldloca.s V_5 + IL_0082: call "int System.ReadOnlySpan.Length.get" + IL_0087: add + IL_0088: stloc.2 + IL_0089: ldloc.3 + IL_008a: ldc.i4.0 + IL_008b: call "void CollectionExtensions.Report(object, bool)" + IL_0090: ret + } + """); + } + [Fact] public void MDArrayToList_Spreads() { @@ -30093,7 +30245,7 @@ .maxstack 5 .locals init (nint[] V_0, System.Span V_1, int V_2, - System.Span V_3) + System.ReadOnlySpan V_3) IL_0000: ldarg.1 IL_0001: stloc.0 IL_0002: newobj "System.Collections.Generic.List..ctor()" @@ -30109,17 +30261,17 @@ .locals init (nint[] V_0, IL_0018: stloc.2 IL_0019: ldloca.s V_3 IL_001b: ldloc.0 - IL_001c: call "System.Span..ctor(nint[])" + IL_001c: call "System.ReadOnlySpan..ctor(nint[])" IL_0021: ldloca.s V_3 IL_0023: ldloca.s V_1 IL_0025: ldloc.2 IL_0026: ldloca.s V_3 - IL_0028: call "int System.Span.Length.get" + IL_0028: call "int System.ReadOnlySpan.Length.get" IL_002d: call "System.Span System.Span.Slice(int, int)" - IL_0032: call "void System.Span.CopyTo(System.Span)" + IL_0032: call "void System.ReadOnlySpan.CopyTo(System.Span)" IL_0037: ldloc.2 IL_0038: ldloca.s V_3 - IL_003a: call "int System.Span.Length.get" + IL_003a: call "int System.ReadOnlySpan.Length.get" IL_003f: add IL_0040: stloc.2 IL_0041: ret @@ -30172,7 +30324,7 @@ .locals init (int V_0, int[] V_2, int[] V_3, System.Span V_4, - System.Span V_5, + System.ReadOnlySpan V_5, System.Runtime.CompilerServices.TaskAwaiter V_6, System.Span V_7, System.Exception V_8) @@ -30268,7 +30420,7 @@ .locals init (int V_0, IL_00ea: add IL_00eb: stfld "int C.
d__0.<>7__wrap4" IL_00f0: ldloc.3 - IL_00f1: newobj "System.Span..ctor(int[])" + IL_00f1: newobj "System.ReadOnlySpan..ctor(int[])" IL_00f6: stloc.s V_5 IL_00f8: ldloca.s V_5 IL_00fa: ldarg.0 @@ -30279,14 +30431,14 @@ .locals init (int V_0, IL_0109: ldarg.0 IL_010a: ldfld "int C.
d__0.<>7__wrap4" IL_010f: ldloca.s V_5 - IL_0111: call "int System.Span.Length.get" + IL_0111: call "int System.ReadOnlySpan.Length.get" IL_0116: call "System.Span System.Span.Slice(int, int)" - IL_011b: call "void System.Span.CopyTo(System.Span)" + IL_011b: call "void System.ReadOnlySpan.CopyTo(System.Span)" IL_0120: ldarg.0 IL_0121: ldarg.0 IL_0122: ldfld "int C.
d__0.<>7__wrap4" IL_0127: ldloca.s V_5 - IL_0129: call "int System.Span.Length.get" + IL_0129: call "int System.ReadOnlySpan.Length.get" IL_012e: add IL_012f: stfld "int C.
d__0.<>7__wrap4" IL_0134: ldarg.0 @@ -30408,7 +30560,7 @@ static void Main() .maxstack 3 .locals init (int[] V_0, //arr System.Span V_1, //span - System.Span V_2) + System.ReadOnlySpan V_2) IL_0000: ldc.i4.3 IL_0001: newarr "int" IL_0006: dup @@ -30420,10 +30572,10 @@ .locals init (int[] V_0, //arr IL_0014: call "void CollectionExtensions.Report(object, bool)" IL_0019: ldloca.s V_1 IL_001b: ldloc.0 - IL_001c: newobj "System.Span..ctor(int[])" + IL_001c: newobj "System.ReadOnlySpan..ctor(int[])" IL_0021: stloc.2 IL_0022: ldloca.s V_2 - IL_0024: call "int[] System.Span.ToArray()" + IL_0024: call "int[] System.ReadOnlySpan.ToArray()" IL_0029: call "System.Span..ctor(int[])" IL_002e: ldloca.s V_1 IL_0030: call "void CollectionExtensions.Report(in System.Span)" @@ -30455,7 +30607,7 @@ static void Main() { // Code size 49 (0x31) .maxstack 3 - .locals init (System.Span V_0) + .locals init (System.ReadOnlySpan V_0) IL_0000: ldc.i4.3 IL_0001: newarr "int" IL_0006: dup @@ -30464,10 +30616,10 @@ .locals init (System.Span V_0) IL_0011: dup IL_0012: ldc.i4.0 IL_0013: call "void CollectionExtensions.Report(object, bool)" - IL_0018: newobj "System.Span..ctor(int[])" + IL_0018: newobj "System.ReadOnlySpan..ctor(int[])" IL_001d: stloc.0 IL_001e: ldloca.s V_0 - IL_0020: call "int[] System.Span.ToArray()" + IL_0020: call "int[] System.ReadOnlySpan.ToArray()" IL_0025: newobj "<>z__ReadOnlyArray..ctor(int[])" IL_002a: ldc.i4.0 IL_002b: call "void CollectionExtensions.Report(object, bool)" From 7b06c6fe36f791f08b94c06388b71d0ce14374cd Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Tue, 23 Jan 2024 17:50:57 -0800 Subject: [PATCH 2/3] Address feedback --- .../LocalRewriter_CollectionExpression.cs | 16 +- .../Semantics/CollectionExpressionTests.cs | 462 ++++++++++++++++++ 2 files changed, 475 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 43e94d7c2c6e5..ba98b7fccd41a 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs @@ -667,10 +667,20 @@ bool tryGetToArrayMethod(TypeSymbol spreadTypeOriginalDefinition, WellKnownType arrayType); } + /// + /// For the purpose of optimization, conversions to ReadOnlySpan and/or Span are known on the following types: + /// System.Array, System.Span, System.ReadOnlySpan, System.Collections.Immutable.ImmutableArray, and System.Collections.Generic.List. + /// + /// Not-null if non-identity conversion was found. /// - /// If is , will only return true with a conversion to writable Span. - /// Otherwise, will return a conversion directly to ReadOnlySpan if one is known, and may return a conversion to Span if no direct conversion to ReadOnlySpan is known, or if the type is already a Span. - /// If non-identity conversion, also returns a non-null . + /// If is 'true', will only return 'true' with a conversion to Span. + /// If is 'false', may return either a conversion to ReadOnlySpan or to Span, depending on the source type. + /// For System.Array and 'false' argument for , only a conversion to ReadOnlySpan may be returned. + /// For System.Array and 'true' argument for , only a conversion to Span may be returned. + /// For System.Span, only a conversion to System.Span is may be returned. + /// For System.ReadOnlySpan, only a conversion to System.ReadOnlySpan may be returned. + /// For System.Collections.Immutable.ImmutableArray, only a conversion to System.ReadOnlySpan may be returned. + /// For System.Collections.Generic.List, only a conversion to System.Span may be returned. /// /// We are assuming that the well-known types we are converting to/from do not have constraints on their type parameters. private bool TryGetSpanConversion(TypeSymbol type, bool writableOnly, out MethodSymbol? asSpanMethod) diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs index 27dbd552a9b77..7bf737e63df3a 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs @@ -29703,6 +29703,93 @@ .locals init (Base[] V_0, """); } + [Fact] + public void ArrayToArray_Covariant_SingleSpread_ReadOnlySpanCtorMissing() + { + var source = """ + class Base { } + class Derived : Base { } + + class C + { + static void Main() + { + Base[] array = new Derived[] { new Derived() }; + array.Report(); + + Base[] copy = [..array]; + copy.Report(); + } + } + """; + + // In the event that the ReadOnlySpan ctor is missing, we do not fall back to converting the array spread value to Span. + // Instead, we lower the spread without optimizing it. + var comp = CreateCompilation(new[] { source, s_collectionExtensionsWithSpan }, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + comp.MakeMemberMissing(WellKnownMember.System_ReadOnlySpan_T__ctor_Array); + + var verifier = CompileAndVerify(comp, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("[Derived], [Derived], ")); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.Main", """ + { + // Code size 71 (0x47) + .maxstack 4 + .locals init (Base[] V_0, + int V_1, + Base[] V_2, + int V_3, + Base V_4) + IL_0000: ldc.i4.1 + IL_0001: newarr "Derived" + IL_0006: dup + IL_0007: ldc.i4.0 + IL_0008: newobj "Derived..ctor()" + IL_000d: stelem.ref + IL_000e: stloc.0 + IL_000f: ldloc.0 + IL_0010: dup + IL_0011: ldc.i4.0 + IL_0012: call "void CollectionExtensions.Report(object, bool)" + IL_0017: ldc.i4.0 + IL_0018: stloc.1 + IL_0019: dup + IL_001a: ldlen + IL_001b: conv.i4 + IL_001c: newarr "Base" + IL_0021: stloc.0 + IL_0022: stloc.2 + IL_0023: ldc.i4.0 + IL_0024: stloc.3 + IL_0025: br.s IL_0039 + IL_0027: ldloc.2 + IL_0028: ldloc.3 + IL_0029: ldelem.ref + IL_002a: stloc.s V_4 + IL_002c: ldloc.0 + IL_002d: ldloc.1 + IL_002e: ldloc.s V_4 + IL_0030: stelem.ref + IL_0031: ldloc.1 + IL_0032: ldc.i4.1 + IL_0033: add + IL_0034: stloc.1 + IL_0035: ldloc.3 + IL_0036: ldc.i4.1 + IL_0037: add + IL_0038: stloc.3 + IL_0039: ldloc.3 + IL_003a: ldloc.2 + IL_003b: ldlen + IL_003c: conv.i4 + IL_003d: blt.s IL_0027 + IL_003f: ldloc.0 + IL_0040: ldc.i4.0 + IL_0041: call "void CollectionExtensions.Report(object, bool)" + IL_0046: ret + } + """); + } + [Fact] public void ArrayToArray_Covariant_MultipleSpreads() { @@ -30200,6 +30287,381 @@ .locals init (int V_0, //i """); } + [Fact] + public void SingleSpread_WellKnownMemberMissing() + { + var source = """ + class C + { + static void Main() + { + int[] arr = [1, 2, 3]; + arr.Report(); + int[] arr1 = [..arr]; + arr1.Report(); + } + } + """; + + var comp = CreateCompilation([source, s_collectionExtensions], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + comp.VerifyEmitDiagnostics(); + + var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [1, 2, 3],")); + verifier.VerifyIL("C.Main", """ + { + // Code size 44 (0x2c) + .maxstack 3 + .locals init (System.ReadOnlySpan V_0) + IL_0000: ldc.i4.3 + IL_0001: newarr "int" + IL_0006: dup + IL_0007: ldtoken ".__StaticArrayInitTypeSize=12 .4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D" + IL_000c: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" + IL_0011: dup + IL_0012: ldc.i4.0 + IL_0013: call "void CollectionExtensions.Report(object, bool)" + IL_0018: newobj "System.ReadOnlySpan..ctor(int[])" + IL_001d: stloc.0 + IL_001e: ldloca.s V_0 + IL_0020: call "int[] System.ReadOnlySpan.ToArray()" + IL_0025: ldc.i4.0 + IL_0026: call "void CollectionExtensions.Report(object, bool)" + IL_002b: ret + } + """); + + // No ReadOnlySpan(T[]) constructor. Spread optimizations can't be performed. + comp = CreateCompilation([source, s_collectionExtensions], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + comp.MakeMemberMissing(WellKnownMember.System_ReadOnlySpan_T__ctor_Array); + comp.VerifyEmitDiagnostics(); + + verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [1, 2, 3],")); + verifier.VerifyIL("C.Main", """ + { + // Code size 72 (0x48) + .maxstack 3 + .locals init (int V_0, + int[] V_1, + int[] V_2, + int V_3, + int V_4) + IL_0000: ldc.i4.3 + IL_0001: newarr "int" + IL_0006: dup + IL_0007: ldtoken ".__StaticArrayInitTypeSize=12 .4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D" + IL_000c: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" + IL_0011: dup + IL_0012: ldc.i4.0 + IL_0013: call "void CollectionExtensions.Report(object, bool)" + IL_0018: ldc.i4.0 + IL_0019: stloc.0 + IL_001a: dup + IL_001b: ldlen + IL_001c: conv.i4 + IL_001d: newarr "int" + IL_0022: stloc.1 + IL_0023: stloc.2 + IL_0024: ldc.i4.0 + IL_0025: stloc.3 + IL_0026: br.s IL_003a + IL_0028: ldloc.2 + IL_0029: ldloc.3 + IL_002a: ldelem.i4 + IL_002b: stloc.s V_4 + IL_002d: ldloc.1 + IL_002e: ldloc.0 + IL_002f: ldloc.s V_4 + IL_0031: stelem.i4 + IL_0032: ldloc.0 + IL_0033: ldc.i4.1 + IL_0034: add + IL_0035: stloc.0 + IL_0036: ldloc.3 + IL_0037: ldc.i4.1 + IL_0038: add + IL_0039: stloc.3 + IL_003a: ldloc.3 + IL_003b: ldloc.2 + IL_003c: ldlen + IL_003d: conv.i4 + IL_003e: blt.s IL_0028 + IL_0040: ldloc.1 + IL_0041: ldc.i4.0 + IL_0042: call "void CollectionExtensions.Report(object, bool)" + IL_0047: ret + } + """); + + // No ReadOnlySpan.ToArray method. ToArray optimization for single spreads cannot be performed, but CopyTo optimization still can. + comp = CreateCompilation([source, s_collectionExtensions], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + comp.MakeMemberMissing(WellKnownMember.System_ReadOnlySpan_T__ToArray); + comp.VerifyEmitDiagnostics(); + + verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [1, 2, 3],")); + verifier.VerifyIL("C.Main", """ + { + // Code size 92 (0x5c) + .maxstack 4 + .locals init (int[] V_0, + int V_1, + int[] V_2, + System.ReadOnlySpan V_3, + System.Span V_4) + IL_0000: ldc.i4.3 + IL_0001: newarr "int" + IL_0006: dup + IL_0007: ldtoken ".__StaticArrayInitTypeSize=12 .4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D" + IL_000c: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" + IL_0011: dup + IL_0012: ldc.i4.0 + IL_0013: call "void CollectionExtensions.Report(object, bool)" + IL_0018: stloc.0 + IL_0019: ldc.i4.0 + IL_001a: stloc.1 + IL_001b: ldloc.0 + IL_001c: ldlen + IL_001d: conv.i4 + IL_001e: newarr "int" + IL_0023: stloc.2 + IL_0024: ldloca.s V_3 + IL_0026: ldloc.0 + IL_0027: call "System.ReadOnlySpan..ctor(int[])" + IL_002c: ldloca.s V_3 + IL_002e: ldloc.2 + IL_002f: newobj "System.Span..ctor(int[])" + IL_0034: stloc.s V_4 + IL_0036: ldloca.s V_4 + IL_0038: ldloc.1 + IL_0039: ldloca.s V_3 + IL_003b: call "int System.ReadOnlySpan.Length.get" + IL_0040: call "System.Span System.Span.Slice(int, int)" + IL_0045: call "void System.ReadOnlySpan.CopyTo(System.Span)" + IL_004a: ldloc.1 + IL_004b: ldloca.s V_3 + IL_004d: call "int System.ReadOnlySpan.Length.get" + IL_0052: add + IL_0053: stloc.1 + IL_0054: ldloc.2 + IL_0055: ldc.i4.0 + IL_0056: call "void CollectionExtensions.Report(object, bool)" + IL_005b: ret + } + """); + } + + [Fact] + public void MultipleSpreads_WellKnownMemberMissing() + { + var source = """ + class C + { + static void Main() + { + int[] arr = [1, 2]; + arr.Report(); + int[] arr1 = [..arr, ..arr]; + arr1.Report(); + } + } + """; + + var comp = CreateCompilation([source, s_collectionExtensions], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + comp.VerifyEmitDiagnostics(); + + var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2], [1, 2, 1, 2],")); + verifier.VerifyIL("C.Main", """ + { + // Code size 143 (0x8f) + .maxstack 4 + .locals init (int[] V_0, + int[] V_1, + int V_2, + int[] V_3, + System.ReadOnlySpan V_4, + System.ReadOnlySpan V_5, + System.Span V_6) + IL_0000: ldc.i4.2 + IL_0001: newarr "int" + IL_0006: dup + IL_0007: ldc.i4.0 + IL_0008: ldc.i4.1 + IL_0009: stelem.i4 + IL_000a: dup + IL_000b: ldc.i4.1 + IL_000c: ldc.i4.2 + IL_000d: stelem.i4 + IL_000e: dup + IL_000f: ldc.i4.0 + IL_0010: call "void CollectionExtensions.Report(object, bool)" + IL_0015: dup + IL_0016: stloc.0 + IL_0017: stloc.1 + IL_0018: ldc.i4.0 + IL_0019: stloc.2 + IL_001a: ldloc.0 + IL_001b: ldlen + IL_001c: conv.i4 + IL_001d: ldloc.1 + IL_001e: ldlen + IL_001f: conv.i4 + IL_0020: add + IL_0021: newarr "int" + IL_0026: stloc.3 + IL_0027: ldloca.s V_4 + IL_0029: ldloc.0 + IL_002a: call "System.ReadOnlySpan..ctor(int[])" + IL_002f: ldloca.s V_4 + IL_0031: ldloc.3 + IL_0032: newobj "System.Span..ctor(int[])" + IL_0037: stloc.s V_6 + IL_0039: ldloca.s V_6 + IL_003b: ldloc.2 + IL_003c: ldloca.s V_4 + IL_003e: call "int System.ReadOnlySpan.Length.get" + IL_0043: call "System.Span System.Span.Slice(int, int)" + IL_0048: call "void System.ReadOnlySpan.CopyTo(System.Span)" + IL_004d: ldloc.2 + IL_004e: ldloca.s V_4 + IL_0050: call "int System.ReadOnlySpan.Length.get" + IL_0055: add + IL_0056: stloc.2 + IL_0057: ldloca.s V_5 + IL_0059: ldloc.1 + IL_005a: call "System.ReadOnlySpan..ctor(int[])" + IL_005f: ldloca.s V_5 + IL_0061: ldloc.3 + IL_0062: newobj "System.Span..ctor(int[])" + IL_0067: stloc.s V_6 + IL_0069: ldloca.s V_6 + IL_006b: ldloc.2 + IL_006c: ldloca.s V_5 + IL_006e: call "int System.ReadOnlySpan.Length.get" + IL_0073: call "System.Span System.Span.Slice(int, int)" + IL_0078: call "void System.ReadOnlySpan.CopyTo(System.Span)" + IL_007d: ldloc.2 + IL_007e: ldloca.s V_5 + IL_0080: call "int System.ReadOnlySpan.Length.get" + IL_0085: add + IL_0086: stloc.2 + IL_0087: ldloc.3 + IL_0088: ldc.i4.0 + IL_0089: call "void CollectionExtensions.Report(object, bool)" + IL_008e: ret + } + """); + + verifyMissing(WellKnownMember.System_ReadOnlySpan_T__ctor_Array); + verifyMissing(WellKnownMember.System_ReadOnlySpan_T__get_Length); + verifyMissing(WellKnownMember.System_ReadOnlySpan_T__CopyTo_Span_T); + verifyMissing(WellKnownMember.System_Span_T__ctor_Array); + verifyMissing(WellKnownMember.System_Span_T__Slice_Int_Int); + + void verifyMissing(WellKnownMember wellKnownMember) + { + var comp = CreateCompilation([source, s_collectionExtensions], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + comp.MakeMemberMissing(wellKnownMember); + comp.VerifyEmitDiagnostics(); + + var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2], [1, 2, 1, 2],")); + verifier.VerifyIL("C.Main", """ + { + // Code size 123 (0x7b) + .maxstack 4 + .locals init (int[] V_0, + int[] V_1, + int V_2, + int[] V_3, + int[] V_4, + int V_5, + int V_6) + IL_0000: ldc.i4.2 + IL_0001: newarr "int" + IL_0006: dup + IL_0007: ldc.i4.0 + IL_0008: ldc.i4.1 + IL_0009: stelem.i4 + IL_000a: dup + IL_000b: ldc.i4.1 + IL_000c: ldc.i4.2 + IL_000d: stelem.i4 + IL_000e: dup + IL_000f: ldc.i4.0 + IL_0010: call "void CollectionExtensions.Report(object, bool)" + IL_0015: dup + IL_0016: stloc.0 + IL_0017: stloc.1 + IL_0018: ldc.i4.0 + IL_0019: stloc.2 + IL_001a: ldloc.0 + IL_001b: ldlen + IL_001c: conv.i4 + IL_001d: ldloc.1 + IL_001e: ldlen + IL_001f: conv.i4 + IL_0020: add + IL_0021: newarr "int" + IL_0026: stloc.3 + IL_0027: ldloc.0 + IL_0028: stloc.s V_4 + IL_002a: ldc.i4.0 + IL_002b: stloc.s V_5 + IL_002d: br.s IL_0045 + IL_002f: ldloc.s V_4 + IL_0031: ldloc.s V_5 + IL_0033: ldelem.i4 + IL_0034: stloc.s V_6 + IL_0036: ldloc.3 + IL_0037: ldloc.2 + IL_0038: ldloc.s V_6 + IL_003a: stelem.i4 + IL_003b: ldloc.2 + IL_003c: ldc.i4.1 + IL_003d: add + IL_003e: stloc.2 + IL_003f: ldloc.s V_5 + IL_0041: ldc.i4.1 + IL_0042: add + IL_0043: stloc.s V_5 + IL_0045: ldloc.s V_5 + IL_0047: ldloc.s V_4 + IL_0049: ldlen + IL_004a: conv.i4 + IL_004b: blt.s IL_002f + IL_004d: ldloc.1 + IL_004e: stloc.s V_4 + IL_0050: ldc.i4.0 + IL_0051: stloc.s V_5 + IL_0053: br.s IL_006b + IL_0055: ldloc.s V_4 + IL_0057: ldloc.s V_5 + IL_0059: ldelem.i4 + IL_005a: stloc.s V_6 + IL_005c: ldloc.3 + IL_005d: ldloc.2 + IL_005e: ldloc.s V_6 + IL_0060: stelem.i4 + IL_0061: ldloc.2 + IL_0062: ldc.i4.1 + IL_0063: add + IL_0064: stloc.2 + IL_0065: ldloc.s V_5 + IL_0067: ldc.i4.1 + IL_0068: add + IL_0069: stloc.s V_5 + IL_006b: ldloc.s V_5 + IL_006d: ldloc.s V_4 + IL_006f: ldlen + IL_0070: conv.i4 + IL_0071: blt.s IL_0055 + IL_0073: ldloc.3 + IL_0074: ldc.i4.0 + IL_0075: call "void CollectionExtensions.Report(object, bool)" + IL_007a: ret + } + """); + } + } + [Fact] public void Spread_RuntimeEquivalentElement() { From c9ad55be56a7abd3d7514b8703b13d821b631f04 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Thu, 25 Jan 2024 11:16:14 -0800 Subject: [PATCH 3/3] Prefer Verifier.VerifyDiagnostics --- .../Test/Emit2/Semantics/CollectionExpressionTests.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs index 7bf737e63df3a..3803d04ca743d 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs @@ -30304,9 +30304,9 @@ static void Main() """; var comp = CreateCompilation([source, s_collectionExtensions], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); - comp.VerifyEmitDiagnostics(); var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [1, 2, 3],")); + verifier.VerifyDiagnostics(); verifier.VerifyIL("C.Main", """ { // Code size 44 (0x2c) @@ -30333,9 +30333,9 @@ .locals init (System.ReadOnlySpan V_0) // No ReadOnlySpan(T[]) constructor. Spread optimizations can't be performed. comp = CreateCompilation([source, s_collectionExtensions], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); comp.MakeMemberMissing(WellKnownMember.System_ReadOnlySpan_T__ctor_Array); - comp.VerifyEmitDiagnostics(); verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [1, 2, 3],")); + verifier.VerifyDiagnostics(); verifier.VerifyIL("C.Main", """ { // Code size 72 (0x48) @@ -30395,9 +30395,9 @@ .locals init (int V_0, // No ReadOnlySpan.ToArray method. ToArray optimization for single spreads cannot be performed, but CopyTo optimization still can. comp = CreateCompilation([source, s_collectionExtensions], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); comp.MakeMemberMissing(WellKnownMember.System_ReadOnlySpan_T__ToArray); - comp.VerifyEmitDiagnostics(); verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [1, 2, 3],")); + verifier.VerifyDiagnostics(); verifier.VerifyIL("C.Main", """ { // Code size 92 (0x5c) @@ -30466,9 +30466,9 @@ static void Main() """; var comp = CreateCompilation([source, s_collectionExtensions], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); - comp.VerifyEmitDiagnostics(); var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2], [1, 2, 1, 2],")); + verifier.VerifyDiagnostics(); verifier.VerifyIL("C.Main", """ { // Code size 143 (0x8f) @@ -30560,9 +30560,9 @@ void verifyMissing(WellKnownMember wellKnownMember) { var comp = CreateCompilation([source, s_collectionExtensions], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); comp.MakeMemberMissing(wellKnownMember); - comp.VerifyEmitDiagnostics(); var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2], [1, 2, 1, 2],")); + verifier.VerifyDiagnostics(); verifier.VerifyIL("C.Main", """ { // Code size 123 (0x7b)