diff --git a/ICSharpCode.Decompiler.Tests/Semantics/OverloadResolutionTests.cs b/ICSharpCode.Decompiler.Tests/Semantics/OverloadResolutionTests.cs index 500f7981d..4d72b7cdf 100644 --- a/ICSharpCode.Decompiler.Tests/Semantics/OverloadResolutionTests.cs +++ b/ICSharpCode.Decompiler.Tests/Semantics/OverloadResolutionTests.cs @@ -402,17 +402,20 @@ namespace ICSharpCode.Decompiler.Tests.Semantics Assert.That(r.IsAmbiguous); } - static IMethod MakeInMethodIn(ICompilation c, Type parameterType) + static IMethod MakeByRefMethodIn(ICompilation c, Type parameterType, ReferenceKind kind) { var m = new FakeMethod(c, SymbolKind.Method); m.Name = "Method"; m.Parameters = new List { new DefaultParameter(new ByReferenceType(c.FindType(parameterType)), string.Empty, - owner: m, referenceKind: ReferenceKind.In) + owner: m, referenceKind: kind) }; return m; } + static IMethod MakeInMethodIn(ICompilation c, Type parameterType) + => MakeByRefMethodIn(c, parameterType, ReferenceKind.In); + [Test] public void InReadOnlySpanParameter_BindsAnArrayWithoutInButNotWithIn() { @@ -472,6 +475,29 @@ namespace ICSharpCode.Decompiler.Tests.Semantics } } + [Test] + public void RefAndOutParametersNeverBindThroughASpanConversion() + { + // Roslyn: CS1503 for both 'M(ref arr)' against 'ref ReadOnlySpan' and + // 'M(out arr)' against 'out ReadOnlySpan' - ref and out demand the + // parameter's own type; the span conversion does not apply. A value argument + // without the keyword is a passing-mode mismatch regardless of conversions. + var c = spanCompilation.Value; + var arrayArg = new ResolveResult(new ArrayType(c, c.FindType(KnownTypeCode.Int32))); + foreach (var kind in new[] { ReferenceKind.Ref, ReferenceKind.Out }) + { + var byRefArgument = new OverloadResolution(c, new[] { + new ByReferenceResolveResult(arrayArg, kind) + }); + Assert.That(byRefArgument.AddCandidate(MakeByRefMethodIn(c, typeof(ReadOnlySpan), kind)), + Is.Not.EqualTo(OverloadResolutionErrors.None), kind.ToString()); + + var valueArgument = new OverloadResolution(c, new[] { arrayArg }); + Assert.That(valueArgument.AddCandidate(MakeByRefMethodIn(c, typeof(ReadOnlySpan), kind)), + Is.Not.EqualTo(OverloadResolutionErrors.None), kind.ToString()); + } + } + [Test] public void InOverloadIsTheOnlyCandidateWithInAtTheCall() { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/FirstClassSpanTypes.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/FirstClassSpanTypes.cs index 3b01982ca..5bfdd89ed 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/FirstClassSpanTypes.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/FirstClassSpanTypes.cs @@ -104,6 +104,23 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { } + public static void RefSpanOrByValue(ref ReadOnlySpan s) + { + } + + public static void RefSpanOrByValue(ReadOnlySpan s) + { + } + + public static void OutSpanOrByValue(out ReadOnlySpan s) + { + s = default(ReadOnlySpan); + } + + public static void OutSpanOrByValue(ReadOnlySpan s) + { + } + public static void GenericArrayOrReadOnlySpan(T[] a) { } @@ -159,12 +176,25 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty ReadOnlySpanOfObjectOrString(strArr); StringOrReadOnlySpanChar(str); ParamsArrayOrParamsReadOnlySpan(arr); + ParamsArrayOrParamsReadOnlySpan(1, 2, 3); GenericArrayOrReadOnlySpan(arr); InferFromReadOnlySpan(arr); InferFromReadOnlySpan(span); arr.ExtensionOnReadOnlySpan(); } + public static void CallRefOutOrByValue(int[] arr) + { + // A span conversion never binds a ref or out parameter: without the keyword the + // by-value overload wins, with the keyword only the ref/out overload is + // applicable and the keyword must survive decompilation. + ReadOnlySpan s = arr; + RefSpanOrByValue(arr); + RefSpanOrByValue(ref s); + OutSpanOrByValue(arr); + OutSpanOrByValue(out s); + } + public static void CallLosersWithExplicitConversions(int[] arr, string str) { ArrayOrReadOnlySpan((ReadOnlySpan)arr);