diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs index 7b1123a3c62c7..b9d484c2a8075 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs @@ -1634,6 +1634,13 @@ private void ExactInference(TypeWithAnnotations source, TypeWithAnnotations targ return; } + // SPEC: * V is a Span and U is an array type U1[] or a Span + // SPEC: * V is a ReadOnlySpan and U is an array type U1[] or a Span or ReadOnlySpan + if (ExactSpanInference(source.Type, target.Type, ref useSiteInfo)) + { + return; + } + // SPEC: * Otherwise, if V is a constructed type C and U is a constructed // SPEC: type C then an exact inference is made // SPEC: from each Ui to the corresponding Vi. @@ -1690,6 +1697,54 @@ private bool ExactArrayInference(TypeWithAnnotations source, TypeWithAnnotations return true; } + private readonly bool IsFeatureFirstClassSpanEnabled + { + get + { + // Note: when Compilation is null, we assume latest LangVersion. + return _compilation?.IsFeatureEnabled(MessageID.IDS_FeatureFirstClassSpan) != false; + } + } + + private bool ExactSpanInference(TypeSymbol source, TypeSymbol target, ref CompoundUseSiteInfo useSiteInfo) + { + Debug.Assert(source is not null); + Debug.Assert(target is not null); + + if (IsFeatureFirstClassSpanEnabled && ( + // SPEC: * V is a Span and U is an array type U1[] or a Span + ( + target.IsSpan() && + (source.IsSZArray() || source.IsSpan()) + ) || + // SPEC: * V is a ReadOnlySpan and U is an array type U1[] or a Span or ReadOnlySpan + ( + target.IsReadOnlySpan() && + (source.IsSZArray() || source.IsSpan() || source.IsReadOnlySpan()) + ) + )) + { + var sourceElementType = GetSpanOrSZArrayElementType(source); + var targetElementType = GetSpanElementType(target); + ExactInference(sourceElementType, targetElementType, ref useSiteInfo); + return true; + } + + return false; + } + + private static TypeWithAnnotations GetSpanElementType(TypeSymbol type) + { + return ((NamedTypeSymbol)type).TypeArgumentsWithAnnotationsNoUseSiteDiagnostics[0]; + } + + private static TypeWithAnnotations GetSpanOrSZArrayElementType(TypeSymbol type) + { + return type is ArrayTypeSymbol arraySource + ? arraySource.ElementTypeWithAnnotations + : GetSpanElementType(type); + } + private enum ExactOrBoundsKind { Exact, @@ -1922,6 +1977,13 @@ private void LowerBoundInference(TypeWithAnnotations source, TypeWithAnnotations return; } + // SPEC: * V is a Span and U is an array type U1[] or a Span + // SPEC: * V is a ReadOnlySpan and U is an array type U1[] or a Span or ReadOnlySpan + if (LowerBoundSpanInference(source.Type, target.Type, ref useSiteInfo)) + { + return; + } + // UNDONE: At this point we could also do an inference from non-nullable U // UNDONE: to nullable V. // UNDONE: @@ -2055,6 +2117,44 @@ private bool LowerBoundArrayInference(TypeSymbol source, TypeSymbol target, ref return true; } + private bool LowerBoundSpanInference(TypeSymbol source, TypeSymbol target, ref CompoundUseSiteInfo useSiteInfo) + { + Debug.Assert(source is not null); + Debug.Assert(target is not null); + + if (IsFeatureFirstClassSpanEnabled && ( + // SPEC: * V is a Span and U is an array type U1[] or a Span + ( + target.IsSpan() && + (source.IsSZArray() || source.IsSpan()) + ) || + // SPEC: * V is a ReadOnlySpan and U is an array type U1[] or a Span or ReadOnlySpan + ( + target.IsReadOnlySpan() && + (source.IsSZArray() || source.IsSpan() || source.IsReadOnlySpan()) + ) + )) + { + var sourceElementType = GetSpanOrSZArrayElementType(source); + var targetElementType = GetSpanElementType(target); + + // SPEC: * If U1 is not known to be a reference type then an exact inference is made + // SPEC: * If V is a Span, then an exact inference is made + if (!sourceElementType.Type.IsReferenceType || target.IsSpan()) + { + ExactInference(sourceElementType, targetElementType, ref useSiteInfo); + } + else + { + LowerBoundInference(sourceElementType, targetElementType, ref useSiteInfo); + } + + return true; + } + + return false; + } + private bool LowerBoundNullableInference(TypeWithAnnotations source, TypeWithAnnotations target, ref CompoundUseSiteInfo useSiteInfo) { return ExactOrBoundsNullableInference(ExactOrBoundsKind.LowerBound, source, target, ref useSiteInfo); diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs index 751bc89446586..34c99852c445e 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs @@ -105,14 +105,10 @@ private static string Concat(string container, string name) } } """; - internal const string s_collectionExtensionsWithSpan = s_collectionExtensions + + internal const string s_collectionExtensionsWithReadOnlySpan = s_collectionExtensions + """ static partial class CollectionExtensions { - internal static void Report(this in Span s) - { - Report((ReadOnlySpan)s); - } internal static void Report(this in ReadOnlySpan s) { var builder = new StringBuilder(); @@ -129,6 +125,16 @@ internal static void Report(this in ReadOnlySpan s) } } """; + internal const string s_collectionExtensionsWithSpan = s_collectionExtensionsWithReadOnlySpan + + """ + static partial class CollectionExtensions + { + internal static void Report(this in Span s) + { + Report((ReadOnlySpan)s); + } + } + """; [Theory] [InlineData(LanguageVersion.CSharp11)] @@ -8155,7 +8161,7 @@ static void F({{spreadType}} x) """; var verifier = CompileAndVerify( - new[] { source, s_collectionExtensionsWithSpan }, + new[] { source, s_collectionExtensionsWithReadOnlySpan }, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70, verify: Verification.Skipped, @@ -8182,32 +8188,37 @@ .maxstack 3 ("IEnumerable", "int[]") => """ { - // Code size 24 (0x18) + // Code size 31 (0x1f) .maxstack 3 + .locals init (System.ReadOnlySpan V_0) 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: callvirt "int[] System.Collections.Generic.List.ToArray()" - IL_0011: ldc.i4.0 - IL_0012: call "void CollectionExtensions.Report(object, bool)" - IL_0017: ret + IL_0011: call "System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(int[])" + IL_0016: stloc.0 + IL_0017: ldloca.s V_0 + IL_0019: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" + IL_001e: ret } """, ("int[]", "int[]") => """ { - // Code size 21 (0x15) - .maxstack 2 + // Code size 28 (0x1c) + .maxstack 1 .locals init (System.ReadOnlySpan V_0) IL_0000: ldarg.0 IL_0001: newobj "System.ReadOnlySpan..ctor(int[])" IL_0006: stloc.0 IL_0007: ldloca.s V_0 IL_0009: call "int[] System.ReadOnlySpan.ToArray()" - IL_000e: ldc.i4.0 - IL_000f: call "void CollectionExtensions.Report(object, bool)" - IL_0014: ret + IL_000e: call "System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(int[])" + IL_0013: stloc.0 + IL_0014: ldloca.s V_0 + IL_0016: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" + IL_001b: ret } """, ("ReadOnlySpan", "ReadOnlySpan") => @@ -32096,12 +32107,31 @@ void M() """; CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics( - // (9,9): error CS1929: 'Span' does not contain a definition for 'SequenceEqual' and the best extension method overload 'MemoryExtensions.SequenceEqual(ReadOnlySpan, ReadOnlySpan)' requires a receiver of type 'System.ReadOnlySpan' + // (9,20): error CS0411: The type arguments for method 'MemoryExtensions.SequenceEqual(ReadOnlySpan, ReadOnlySpan)' cannot be inferred from the usage. Try specifying the type arguments explicitly. // a.AsSpan().SequenceEqual([0, 1]); - Diagnostic(ErrorCode.ERR_BadInstanceArgType, "a.AsSpan()").WithArguments("System.Span", "SequenceEqual", "System.MemoryExtensions.SequenceEqual(System.ReadOnlySpan, System.ReadOnlySpan)", "System.ReadOnlySpan").WithLocation(9, 9) + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "SequenceEqual").WithArguments("System.MemoryExtensions.SequenceEqual(System.ReadOnlySpan, System.ReadOnlySpan)").WithLocation(9, 20) ); } + [Fact] + public void ElementNullability_Inference_BadCall_02() + { + string src = """ + #nullable enable + using System; + + class C + { + void M() + { + byte[] a = [1, 2]; + a.AsSpan().SequenceEqual([0, 1]); + } + } + """; + CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(); + } + [Fact] public void SpreadNullability() { @@ -32333,10 +32363,11 @@ static void Main() verifier.VerifyDiagnostics(); verifier.VerifyIL("C.Main", """ { - // Code size 68 (0x44) + // Code size 75 (0x4b) .maxstack 2 .locals init (System.Span V_0, //li - <>y__InlineArray3 V_1) + <>y__InlineArray3 V_1, + System.Span V_2) IL_0000: ldloca.s V_1 IL_0002: initobj "<>y__InlineArray3" IL_0008: ldloca.s V_1 @@ -32362,9 +32393,11 @@ .locals init (System.Span V_0, //li IL_0031: call "void CollectionExtensions.Report(in System.Span)" IL_0036: ldloca.s V_0 IL_0038: call "int[] System.Span.ToArray()" - IL_003d: ldc.i4.0 - IL_003e: call "void CollectionExtensions.Report(object, bool)" - IL_0043: ret + IL_003d: call "System.Span System.Span.op_Implicit(int[])" + IL_0042: stloc.2 + IL_0043: ldloca.s V_2 + IL_0045: call "void CollectionExtensions.Report(in System.Span)" + IL_004a: ret } """); } @@ -32391,9 +32424,10 @@ static void Main() verifier.VerifyDiagnostics(); verifier.VerifyIL("C.Main", """ { - // Code size 32 (0x20) - .maxstack 2 - .locals init (System.ReadOnlySpan V_0) //li + // Code size 39 (0x27) + .maxstack 1 + .locals init (System.ReadOnlySpan V_0, //li + System.Span V_1) IL_0000: ldtoken ".__StaticArrayInitTypeSize=12_Align=4 .4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D4" IL_0005: call "System.ReadOnlySpan System.Runtime.CompilerServices.RuntimeHelpers.CreateSpan(System.RuntimeFieldHandle)" IL_000a: stloc.0 @@ -32401,9 +32435,11 @@ .locals init (System.ReadOnlySpan V_0) //li IL_000d: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" IL_0012: ldloca.s V_0 IL_0014: call "int[] System.ReadOnlySpan.ToArray()" - IL_0019: ldc.i4.0 - IL_001a: call "void CollectionExtensions.Report(object, bool)" - IL_001f: ret + IL_0019: call "System.Span System.Span.op_Implicit(int[])" + IL_001e: stloc.1 + IL_001f: ldloca.s V_1 + IL_0021: call "void CollectionExtensions.Report(in System.Span)" + IL_0026: ret } """); } @@ -32432,7 +32468,7 @@ static void Main() verifier.VerifyDiagnostics(); verifier.VerifyIL("C.Main", """ { - // Code size 162 (0xa2) + // Code size 170 (0xaa) .maxstack 4 .locals init (System.ReadOnlySpan V_0, //li1 System.ReadOnlySpan V_1, //li2 @@ -32495,9 +32531,11 @@ .locals init (System.ReadOnlySpan V_0, //li1 IL_0096: add IL_0097: stloc.s V_4 IL_0099: ldloc.s V_5 - IL_009b: ldc.i4.0 - IL_009c: call "void CollectionExtensions.Report(object, bool)" - IL_00a1: ret + IL_009b: call "System.Span System.Span.op_Implicit(int[])" + IL_00a0: stloc.s V_6 + IL_00a2: ldloca.s V_6 + IL_00a4: call "void CollectionExtensions.Report(in System.Span)" + IL_00a9: ret } """); } @@ -32522,7 +32560,7 @@ static void Main() class D : C { } """; - var verifier = CompileAndVerify(new[] { source, s_collectionExtensionsWithSpan }, expectedOutput: IncludeExpectedOutput("[D, D],"), verify: Verification.Skipped, targetFramework: TargetFramework.Net80); + var verifier = CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: IncludeExpectedOutput("[D, D],"), verify: Verification.Skipped, targetFramework: TargetFramework.Net80); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.Main", """ { @@ -32885,11 +32923,11 @@ static void Main() } """; - var verifier = CompileAndVerify(new[] { source, s_collectionExtensionsWithSpan }, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("[Derived], [Derived], "), targetFramework: TargetFramework.Net80); + var verifier = CompileAndVerify(new[] { source, s_collectionExtensionsWithReadOnlySpan }, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("[Derived], [Derived], "), targetFramework: TargetFramework.Net80); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.Main", """ { - // Code size 43 (0x2b) + // Code size 57 (0x39) .maxstack 4 .locals init (Base[] V_0, System.ReadOnlySpan V_1) @@ -32902,15 +32940,19 @@ .locals init (Base[] V_0, 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 + IL_0011: call "System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(Base[])" + IL_0016: stloc.1 + IL_0017: ldloca.s V_1 + IL_0019: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" + IL_001e: newobj "System.ReadOnlySpan..ctor(Base[])" + IL_0023: stloc.1 + IL_0024: ldloca.s V_1 + IL_0026: call "Base[] System.ReadOnlySpan.ToArray()" + IL_002b: call "System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(Base[])" + IL_0030: stloc.1 + IL_0031: ldloca.s V_1 + IL_0033: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" + IL_0038: ret } """); } @@ -32937,20 +32979,21 @@ static void Main() // 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); + var comp = CreateCompilation(new[] { source, s_collectionExtensionsWithReadOnlySpan }, 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) + // Code size 90 (0x5a) .maxstack 4 .locals init (Base[] V_0, - int V_1, - Base[] V_2, - int V_3, - Base V_4) + System.ReadOnlySpan V_1, + int V_2, + Base[] V_3, + int V_4, + Base V_5) IL_0000: ldc.i4.1 IL_0001: newarr "Derived" IL_0006: dup @@ -32960,44 +33003,48 @@ .locals init (Base[] V_0, 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_0011: call "System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(Base[])" + IL_0016: stloc.1 + IL_0017: ldloca.s V_1 + IL_0019: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" + IL_001e: ldc.i4.0 + IL_001f: stloc.2 + IL_0020: dup + IL_0021: ldlen + IL_0022: conv.i4 + IL_0023: newarr "Base" + IL_0028: stloc.0 + IL_0029: stloc.3 + IL_002a: ldc.i4.0 + IL_002b: stloc.s V_4 + IL_002d: br.s IL_0044 + IL_002f: ldloc.3 + IL_0030: ldloc.s V_4 + IL_0032: ldelem.ref + IL_0033: stloc.s V_5 + IL_0035: ldloc.0 + IL_0036: ldloc.2 + IL_0037: ldloc.s V_5 + IL_0039: stelem.ref 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 + IL_003b: ldc.i4.1 + IL_003c: add + IL_003d: stloc.2 + IL_003e: ldloc.s V_4 + IL_0040: ldc.i4.1 + IL_0041: add + IL_0042: stloc.s V_4 + IL_0044: ldloc.s V_4 + IL_0046: ldloc.3 + IL_0047: ldlen + IL_0048: conv.i4 + IL_0049: blt.s IL_002f + IL_004b: ldloc.0 + IL_004c: call "System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(Base[])" + IL_0051: stloc.1 + IL_0052: ldloca.s V_1 + IL_0054: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" + IL_0059: ret } """); } @@ -33022,17 +33069,17 @@ static void Main() } """; - var verifier = CompileAndVerify(new[] { source, s_collectionExtensionsWithSpan }, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("[Derived], [Derived, Derived],"), targetFramework: TargetFramework.Net80); + var verifier = CompileAndVerify(new[] { source, s_collectionExtensionsWithReadOnlySpan }, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("[Derived], [Derived, Derived],"), targetFramework: TargetFramework.Net80); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.Main", """ { - // Code size 145 (0x91) + // Code size 164 (0xa4) .maxstack 4 .locals init (Base[] V_0, - Base[] V_1, - int V_2, - Base[] V_3, - System.ReadOnlySpan V_4, + System.ReadOnlySpan V_1, + Base[] V_2, + int V_3, + Base[] V_4, System.ReadOnlySpan V_5, System.Span V_6) IL_0000: ldc.i4.1 @@ -33044,62 +33091,66 @@ .locals init (Base[] V_0, 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_0011: call "System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(Base[])" + IL_0016: stloc.1 + IL_0017: ldloca.s V_1 + IL_0019: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" + IL_001e: dup + IL_001f: stloc.0 + IL_0020: stloc.2 + IL_0021: ldc.i4.0 + IL_0022: stloc.3 + IL_0023: ldloc.0 + IL_0024: ldlen + IL_0025: conv.i4 + IL_0026: ldloc.2 + IL_0027: ldlen + IL_0028: conv.i4 + IL_0029: add + IL_002a: newarr "Base" + IL_002f: stloc.s V_4 + IL_0031: ldloca.s V_1 + IL_0033: ldloc.0 + IL_0034: call "System.ReadOnlySpan..ctor(Base[])" + IL_0039: ldloca.s V_1 + IL_003b: ldloc.s V_4 + IL_003d: newobj "System.Span..ctor(Base[])" + IL_0042: stloc.s V_6 + IL_0044: ldloca.s V_6 + IL_0046: ldloc.3 + IL_0047: ldloca.s V_1 + IL_0049: call "int System.ReadOnlySpan.Length.get" + IL_004e: call "System.Span System.Span.Slice(int, int)" + IL_0053: call "void System.ReadOnlySpan.CopyTo(System.Span)" + IL_0058: ldloc.3 + IL_0059: ldloca.s V_1 + IL_005b: call "int System.ReadOnlySpan.Length.get" + IL_0060: add + IL_0061: stloc.3 + IL_0062: ldloca.s V_5 + IL_0064: ldloc.2 + IL_0065: call "System.ReadOnlySpan..ctor(Base[])" + IL_006a: ldloca.s V_5 + IL_006c: ldloc.s V_4 + IL_006e: newobj "System.Span..ctor(Base[])" + IL_0073: stloc.s V_6 + IL_0075: ldloca.s V_6 + IL_0077: ldloc.3 + IL_0078: ldloca.s V_5 + IL_007a: call "int System.ReadOnlySpan.Length.get" + IL_007f: call "System.Span System.Span.Slice(int, int)" + IL_0084: call "void System.ReadOnlySpan.CopyTo(System.Span)" IL_0089: ldloc.3 - IL_008a: ldc.i4.0 - IL_008b: call "void CollectionExtensions.Report(object, bool)" - IL_0090: ret + IL_008a: ldloca.s V_5 + IL_008c: call "int System.ReadOnlySpan.Length.get" + IL_0091: add + IL_0092: stloc.3 + IL_0093: ldloc.s V_4 + IL_0095: call "System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(Base[])" + IL_009a: stloc.s V_5 + IL_009c: ldloca.s V_5 + IL_009e: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" + IL_00a3: ret } """); } @@ -34000,7 +34051,7 @@ static async Task M2() } """; - var verifier = CompileAndVerify(new[] { source, s_collectionExtensionsWithSpan }, expectedOutput: IncludeExpectedOutput("[1, 2, 2],"), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); + var verifier = CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: IncludeExpectedOutput("[1, 2, 2],"), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.
d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", """ { @@ -34243,11 +34294,12 @@ static void Main() verifier.VerifyDiagnostics(); verifier.VerifyIL("C.Main", """ { - // Code size 54 (0x36) + // Code size 61 (0x3d) .maxstack 3 .locals init (int[] V_0, //arr System.Span V_1, //span - System.ReadOnlySpan V_2) + System.Span V_2, + System.ReadOnlySpan V_3) IL_0000: ldc.i4.3 IL_0001: newarr "int" IL_0006: dup @@ -34255,18 +34307,20 @@ .locals init (int[] V_0, //arr IL_000c: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" IL_0011: stloc.0 IL_0012: ldloc.0 - IL_0013: ldc.i4.0 - IL_0014: call "void CollectionExtensions.Report(object, bool)" - IL_0019: ldloca.s V_1 - IL_001b: ldloc.0 - IL_001c: newobj "System.ReadOnlySpan..ctor(int[])" - IL_0021: stloc.2 - IL_0022: ldloca.s V_2 - 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)" - IL_0035: ret + IL_0013: call "System.Span System.Span.op_Implicit(int[])" + IL_0018: stloc.2 + IL_0019: ldloca.s V_2 + IL_001b: call "void CollectionExtensions.Report(in System.Span)" + IL_0020: ldloca.s V_1 + IL_0022: ldloc.0 + IL_0023: newobj "System.ReadOnlySpan..ctor(int[])" + IL_0028: stloc.3 + IL_0029: ldloca.s V_3 + IL_002b: call "int[] System.ReadOnlySpan.ToArray()" + IL_0030: call "System.Span..ctor(int[])" + IL_0035: ldloca.s V_1 + IL_0037: call "void CollectionExtensions.Report(in System.Span)" + IL_003c: ret } """); } @@ -34288,7 +34342,7 @@ static void Main() } """; - var verifier = CompileAndVerify(new[] { source, s_collectionExtensionsWithSpan }, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [1, 2, 3],"), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); + var verifier = CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [1, 2, 3],"), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.Main", """ { diff --git a/src/Compilers/CSharp/Test/Emit3/FirstClassSpanTests.cs b/src/Compilers/CSharp/Test/Emit3/FirstClassSpanTests.cs index 92ae3f65f1623..81bd6d9fe1c49 100644 --- a/src/Compilers/CSharp/Test/Emit3/FirstClassSpanTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/FirstClassSpanTests.cs @@ -279,6 +279,125 @@ static class E CompileAndVerify(comp, expectedOutput: "3").VerifyDiagnostics(); } + [Fact] + public void BreakingChange_TypeInference_SpanVsIEnumerable_01() + { + var source = """ + using System; + using System.Collections.Generic; + + class C + { + void M(int[] a) + { + foreach (var x in a.R()) { } + } + } + + static class E + { + public static void R(this Span s) => throw null; + public static IEnumerable R(this IEnumerable e) => throw null; + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics(); + + var expectedDiagnostics = new[] + { + // (8,27): error CS1579: foreach statement cannot operate on variables of type 'void' because 'void' does not contain a public instance or extension definition for 'GetEnumerator' + // foreach (var x in a.R()) { } + Diagnostic(ErrorCode.ERR_ForEachMissingMember, "a.R()").WithArguments("void", "GetEnumerator").WithLocation(8, 27) + }; + + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilationWithSpanAndMemoryExtensions(source).VerifyDiagnostics(expectedDiagnostics); + } + + [Theory, MemberData(nameof(LangVersions))] + public void BreakingChange_TypeInference_SpanVsIEnumerable_01_Workaround(LanguageVersion langVersion) + { + var source = """ + using System; + using System.Collections.Generic; + + class C + { + void M(int[] a) + { + foreach (var x in a.R()) { } + } + } + + static class E + { + public static void R(this Span s) => throw null; + public static IEnumerable R(this IEnumerable e) => throw null; + public static IEnumerable R(this T[] a) => throw null; + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics(); + } + + [Fact] + public void BreakingChange_TypeInference_SpanVsIEnumerable_02() + { + var source = """ + using System; + using System.Collections.Generic; + + string[] s = new[] { "a" }; + object[] o = s; + + try { C.R(o); } catch { Console.Write(3); } + + static class C + { + public static void R(IEnumerable e) => Console.Write(1); + public static void R(Span s) => Console.Write(2); + } + """; + var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13); + CompileAndVerify(comp, expectedOutput: "1").VerifyDiagnostics(); + + var expectedOutput = ExecutionConditionUtil.IsCoreClr ? "3" : "2"; + + comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + + comp = CreateCompilationWithSpanAndMemoryExtensions(source); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + } + + [Fact] + public void BreakingChange_Conversion_SpanVsIEnumerable() + { + var source = """ + using System; + using System.Collections.Generic; + + string[] s = new[] { "a" }; + object[] o = s; + + try { o.R(); } catch { Console.Write(3); } + + static class E + { + public static void R(this IEnumerable e) => Console.Write(1); + public static void R(this Span s) => Console.Write(2); + } + """; + var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13); + CompileAndVerify(comp, expectedOutput: "1").VerifyDiagnostics(); + + var expectedOutput = ExecutionConditionUtil.IsCoreClr ? "3" : "2"; + + comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + + comp = CreateCompilationWithSpanAndMemoryExtensions(source); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + } + [Theory, CombinatorialData] public void Conversion_Array_Span_Implicit( [CombinatorialLangVersions] LanguageVersion langVersion, @@ -4305,18 +4424,14 @@ static class C // C.R(x => a.M(x)); Diagnostic(ErrorCode.ERR_BadInstanceArgType, "a").WithArguments("int[]", "M", "C.M(System.Span, int)", "System.Span").WithLocation(7, 10)); - // PROTOTYPE: Some of these need type inference to work. var expectedDiagnostics = new[] { - // (4,5): error CS1061: 'int[]' does not contain a definition for 'M' and no accessible extension method 'M' accepting a first argument of type 'int[]' could be found (are you missing a using directive or an assembly reference?) + // (4,5): error CS1503: Argument 1: cannot convert from 'method group' to 'System.Func' // C.R(a.M); - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "a.M").WithArguments("int[]", "M").WithLocation(4, 5), + Diagnostic(ErrorCode.ERR_BadArgType, "a.M").WithArguments("1", "method group", "System.Func").WithLocation(4, 5), // (5,5): error CS1503: Argument 1: cannot convert from 'method group' to 'System.Func' // C.R(a.M); - Diagnostic(ErrorCode.ERR_BadArgType, "a.M").WithArguments("1", "method group", "System.Func").WithLocation(5, 5), - // (6,12): error CS1061: 'int[]' does not contain a definition for 'M' and no accessible extension method 'M' accepting a first argument of type 'int[]' could be found (are you missing a using directive or an assembly reference?) - // C.R(x => a.M(x)); - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M").WithArguments("int[]", "M").WithLocation(6, 12) + Diagnostic(ErrorCode.ERR_BadArgType, "a.M").WithArguments("1", "method group", "System.Func").WithLocation(5, 5) }; CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); @@ -4336,6 +4451,8 @@ public void Conversion_Array_Span_Implicit_MethodGroup_ExtensionMethodReceiver_G var d4 = a.M; var d5 = x => a.M(x); var d6 = (int x) => a.M(x); + Func d7 = a.M; + Func d8 = x => a.M(x); static class C { @@ -4361,26 +4478,31 @@ static class C Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "x => a.M(x)").WithLocation(8, 10), // (9,21): error CS1929: 'int[]' does not contain a definition for 'M' and the best extension method overload 'C.M(Span, int)' requires a receiver of type 'System.Span' // var d6 = (int x) => a.M(x); - Diagnostic(ErrorCode.ERR_BadInstanceArgType, "a").WithArguments("int[]", "M", "C.M(System.Span, int)", "System.Span").WithLocation(9, 21)); + Diagnostic(ErrorCode.ERR_BadInstanceArgType, "a").WithArguments("int[]", "M", "C.M(System.Span, int)", "System.Span").WithLocation(9, 21), + // (10,21): error CS1061: 'int[]' does not contain a definition for 'M' and no accessible extension method 'M' accepting a first argument of type 'int[]' could be found (are you missing a using directive or an assembly reference?) + // Func d7 = a.M; + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "a.M").WithArguments("int[]", "M").WithLocation(10, 21), + // (11,28): error CS1061: 'int[]' does not contain a definition for 'M' and no accessible extension method 'M' accepting a first argument of type 'int[]' could be found (are you missing a using directive or an assembly reference?) + // Func d8 = x => a.M(x); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M").WithArguments("int[]", "M").WithLocation(11, 28)); - // PROTOTYPE: Some of these need type inference to work. var expectedDiagnostics = new[] { - // (4,10): error CS8917: The delegate type could not be inferred. + // (4,12): error CS0123: No overload for 'M' matches delegate 'Func' // var d1 = a.M; - Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "a.M").WithLocation(4, 10), + Diagnostic(ErrorCode.ERR_MethDelegateMismatch, "M").WithArguments("M", "System.Func").WithLocation(4, 12), // (5,10): error CS8917: The delegate type could not be inferred. // var d2 = x => a.M(x); Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "x => a.M(x)").WithLocation(5, 10), - // (6,23): error CS1061: 'int[]' does not contain a definition for 'M' and no accessible extension method 'M' accepting a first argument of type 'int[]' could be found (are you missing a using directive or an assembly reference?) - // var d3 = (int x) => a.M(x); - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M").WithArguments("int[]", "M").WithLocation(6, 23), // (7,12): error CS0123: No overload for 'M' matches delegate 'Func' // var d4 = a.M; Diagnostic(ErrorCode.ERR_MethDelegateMismatch, "M").WithArguments("M", "System.Func").WithLocation(7, 12), // (8,10): error CS8917: The delegate type could not be inferred. // var d5 = x => a.M(x); - Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "x => a.M(x)").WithLocation(8, 10) + Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "x => a.M(x)").WithLocation(8, 10), + // (10,23): error CS0123: No overload for 'M' matches delegate 'Func' + // Func d7 = a.M; + Diagnostic(ErrorCode.ERR_MethDelegateMismatch, "M").WithArguments("M", "System.Func").WithLocation(10, 23) }; CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); @@ -5934,11 +6056,27 @@ static class C public static void E(this Span arg) { } } """; - // PROTOTYPE: Needs type inference to work. - CreateCompilationWithSpanAndMemoryExtensions(source).VerifyDiagnostics( + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics( // (4,44): error CS1061: 'int[]' does not contain a definition for 'E' and no accessible extension method 'E' accepting a first argument of type 'int[]' could be found (are you missing a using directive or an assembly reference?) // public static void M(int[] arg) => arg.E(); Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "E").WithArguments("int[]", "E").WithLocation(4, 44)); + + var expectedIl = """ + { + // Code size 12 (0xc) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call "System.Span System.Span.op_Implicit(int[])" + IL_0006: call "void C.E(System.Span)" + IL_000b: ret + } + """; + + var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext); + CompileAndVerify(comp).VerifyDiagnostics().VerifyIL("C.M", expectedIl); + + comp = CreateCompilationWithSpanAndMemoryExtensions(source); + CompileAndVerify(comp).VerifyDiagnostics().VerifyIL("C.M", expectedIl); } [Fact] @@ -5986,9 +6124,8 @@ .maxstack 1 Assert.Equal("System.Int32[]", arrayType.ToTestDisplayString()); // Reduce the extension method with array receiver. - // PROTOTYPE: This needs type inference to work. reduced = unreducedSymbol.ReduceExtensionMethod(arrayType); - Assert.Null(reduced); + Assert.Equal("void System.Span.E()", reduced.ToTestDisplayString()); } [Fact] @@ -6037,9 +6174,8 @@ .maxstack 2 Assert.Equal("System.Int32[]", arrayType.ToTestDisplayString()); // Reduce the extension method with array receiver. - // PROTOTYPE: This needs type inference to work. reduced = unreducedSymbol.ReduceExtensionMethod(arrayType); - Assert.Null(reduced); + Assert.Equal("void System.Span.E(System.Int32 x)", reduced.ToTestDisplayString()); } [Fact] @@ -6088,9 +6224,8 @@ .maxstack 2 Assert.Equal("System.Int32[]", arrayType.ToTestDisplayString()); // Reduce the extension method with array receiver. - // PROTOTYPE: This needs type inference to work. reduced = unreducedSymbol.ReduceExtensionMethod(arrayType); - Assert.Null(reduced); + Assert.Equal("void System.Span.E(System.Int32 x)", reduced.ToTestDisplayString()); } [Fact] @@ -7408,4 +7543,282 @@ static class C comp = CreateCompilationWithSpanAndMemoryExtensions(source); CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); } + + private static readonly SymbolDisplayFormat s_typeArgumentsDisplayFormat = SymbolDisplayFormat.TestFormat + .RemoveMemberOptions(SymbolDisplayMemberOptions.IncludeParameters | SymbolDisplayMemberOptions.IncludeType); + + private static string DisplayInvokedMethodTypeArguments(CSharpCompilation comp) + { + var invocation = comp.SyntaxTrees.Single().GetRoot().DescendantNodes().OfType().Single(); + var model = comp.GetSemanticModel(invocation.SyntaxTree); + var symbol = model.GetSymbolInfo(invocation).Symbol.GetSymbol()!; + return symbol.ToDisplayString(s_typeArgumentsDisplayFormat); + } + + [Fact] + public void TypeInference_Span_Span_01() + { + var source = """ + using System; + class C + { + void M(Span a) => M1(a); + void M1(Span x) { } + } + """; + var comp = CreateCompilationWithSpanAndMemoryExtensions(source).VerifyDiagnostics(); + AssertEx.Equal("C.M1", DisplayInvokedMethodTypeArguments(comp)); + } + + [Fact] + public void TypeInference_Span_Span_02() + { + var source = """ + using System; + class C + { + void M(Span a, object b) => M1(a, b); + void M1(Span x, T y) { } + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source).VerifyDiagnostics( + // (4,41): error CS0411: The type arguments for method 'C.M1(Span, T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // void M(Span a, object b) => M1(a, b); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "M1").WithArguments("C.M1(System.Span, T)").WithLocation(4, 41)); + } + + [Fact] + public void TypeInference_Span_Span_03() + { + var source = """ + using System; + class C + { + void M(Span a, string b) => M1(a, b); + void M1(Span x, T y) { } + } + """; + var comp = CreateCompilationWithSpanAndMemoryExtensions(source).VerifyDiagnostics(); + AssertEx.Equal("C.M1", DisplayInvokedMethodTypeArguments(comp)); + } + + [Fact] + public void TypeInference_Span_Span_04() + { + var source = """ + using System; + class C + { + void M(Span a, object b) => M1(a, b); + void M1(Span x, T y) { } + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source).VerifyDiagnostics( + // (4,43): error CS0411: The type arguments for method 'C.M1(Span, T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // void M(Span a, object b) => M1(a, b); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "M1").WithArguments("C.M1(System.Span, T)").WithLocation(4, 43)); + } + + [Fact] + public void TypeInference_Array_Span_01() + { + var source = """ + using System; + class C + { + void M(int[] a) => M1(a); + void M1(Span x) { } + } + """; + var comp = CreateCompilationWithSpanAndMemoryExtensions(source).VerifyDiagnostics(); + AssertEx.Equal("C.M1", DisplayInvokedMethodTypeArguments(comp)); + } + + [Fact] + public void TypeInference_Array_Span_02() + { + var source = """ + using System; + class C + { + void M(string[] a, object b) => M1(a, b); + void M1(Span x, T y) { } + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source).VerifyDiagnostics( + // (4,37): error CS0411: The type arguments for method 'C.M1(Span, T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // void M(string[] a, object b) => M1(a, b); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "M1").WithArguments("C.M1(System.Span, T)").WithLocation(4, 37)); + } + + [Fact] + public void TypeInference_Array_Span_03() + { + var source = """ + using System; + class C + { + void M(object[] a, string b) => M1(a, b); + void M1(Span x, T y) { } + } + """; + var comp = CreateCompilationWithSpanAndMemoryExtensions(source).VerifyDiagnostics(); + AssertEx.Equal("C.M1", DisplayInvokedMethodTypeArguments(comp)); + } + + [Fact] + public void TypeInference_Span_Array() + { + var source = """ + using System; + class C + { + void M(Span a) => M1(a); + void M1(T[] x) { } + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source).VerifyDiagnostics( + // (4,28): error CS0411: The type arguments for method 'C.M1(T[])' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // void M(Span a) => M1(a); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "M1").WithArguments("C.M1(T[])").WithLocation(4, 28)); + } + + [Fact] + public void TypeInference_Array_ReadOnlySpan_01() + { + var source = """ + using System; + class C + { + void M(int[] a) => M1(a); + void M1(ReadOnlySpan x) { } + } + """; + var comp = CreateCompilationWithSpanAndMemoryExtensions(source).VerifyDiagnostics(); + AssertEx.Equal("C.M1", DisplayInvokedMethodTypeArguments(comp)); + } + + [Fact] + public void TypeInference_Array_ReadOnlySpan_02() + { + var source = """ + using System; + class C + { + void M(string[] a, object b) => M1(a, b); + void M1(ReadOnlySpan x, T y) { } + } + """; + var comp = CreateCompilationWithSpanAndMemoryExtensions(source).VerifyDiagnostics(); + AssertEx.Equal("C.M1", DisplayInvokedMethodTypeArguments(comp)); + } + + [Fact] + public void TypeInference_Array_ReadOnlySpan_03() + { + var source = """ + using System; + class C + { + void M(object[] a, string b) => M1(a, b); + void M1(ReadOnlySpan x, T y) { } + } + """; + var comp = CreateCompilationWithSpanAndMemoryExtensions(source).VerifyDiagnostics(); + AssertEx.Equal("C.M1", DisplayInvokedMethodTypeArguments(comp)); + } + + [Fact] + public void TypeInference_ReadOnlySpan_ReadOnlySpan_01() + { + var source = """ + using System; + class C + { + void M(ReadOnlySpan a) => M1(a); + void M1(ReadOnlySpan x) { } + } + """; + var comp = CreateCompilationWithSpanAndMemoryExtensions(source).VerifyDiagnostics(); + AssertEx.Equal("C.M1", DisplayInvokedMethodTypeArguments(comp)); + } + + [Fact] + public void TypeInference_ReadOnlySpan_ReadOnlySpan_02() + { + var source = """ + using System; + class C + { + void M(ReadOnlySpan a, object b) => M1(a, b); + void M1(ReadOnlySpan x, T y) { } + } + """; + var comp = CreateCompilationWithSpanAndMemoryExtensions(source, targetFramework: TargetFramework.Net90).VerifyDiagnostics(); + AssertEx.Equal("C.M1", DisplayInvokedMethodTypeArguments(comp)); + } + + [Fact] + public void TypeInference_ReadOnlySpan_ReadOnlySpan_03() + { + var source = """ + using System; + class C + { + void M(ReadOnlySpan a, string b) => M1(a, b); + void M1(ReadOnlySpan x, T y) { } + } + """; + var comp = CreateCompilationWithSpanAndMemoryExtensions(source).VerifyDiagnostics(); + AssertEx.Equal("C.M1", DisplayInvokedMethodTypeArguments(comp)); + } + + [Fact] + public void TypeInference_ReadOnlySpan_ReadOnlySpan_04() + { + var source = """ + using System; + class C + { + void M(ReadOnlySpan a, object b) => M1(a, b); + void M1(ReadOnlySpan x, T y) { } + } + """; + var comp = CreateCompilationWithSpanAndMemoryExtensions(source, targetFramework: TargetFramework.Net90).VerifyDiagnostics(); + AssertEx.Equal("C.M1", DisplayInvokedMethodTypeArguments(comp)); + } + + [Fact] + public void TypeInference_ReadOnlySpan_ReadOnlySpan_05() + { + var source = """ + using System; + class C + { + void M(out ReadOnlySpan a, object b) => M1(out a, b); + void M1(out ReadOnlySpan x, T y) => throw null; + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source).VerifyDiagnostics( + // (4,55): error CS0411: The type arguments for method 'C.M1(out ReadOnlySpan, T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // void M(out ReadOnlySpan a, object b) => M1(out a, b); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "M1").WithArguments("C.M1(out System.ReadOnlySpan, T)").WithLocation(4, 55)); + } + + [Fact] + public void TypeInference_ReadOnlySpan_Span() + { + var source = """ + using System; + class C + { + void M(ReadOnlySpan a) => M1(a); + void M1(Span x) { } + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source).VerifyDiagnostics( + // (4,36): error CS0411: The type arguments for method 'C.M1(Span)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // void M(ReadOnlySpan a) => M1(a); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "M1").WithArguments("C.M1(System.Span)").WithLocation(4, 36)); + } } diff --git a/src/Features/CSharp/Portable/CodeRefactorings/UseRecursivePatterns/UseRecursivePatternsCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/CodeRefactorings/UseRecursivePatterns/UseRecursivePatternsCodeRefactoringProvider.cs index d87e358100bfb..0183ef6d626ed 100644 --- a/src/Features/CSharp/Portable/CodeRefactorings/UseRecursivePatterns/UseRecursivePatternsCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/CodeRefactorings/UseRecursivePatterns/UseRecursivePatternsCodeRefactoringProvider.cs @@ -600,7 +600,7 @@ protected override async Task FixAllAsync( // Process all nodes to refactor in reverse to ensure nested nodes // are processed before the outer nodes to refactor. - foreach (var originalNode in nodes.Reverse()) + foreach (var originalNode in Enumerable.Reverse(nodes)) { // Only process nodes fully within a fixAllSpan if (!fixAllSpans.Any(fixAllSpan => fixAllSpan.Contains(originalNode.Span)))