mirror of https://github.com/icsharpcode/ILSpy.git
Browse Source
The C# 14 compiler lowers implicit span conversions to calls - MemoryExtensions.AsSpan(string), ReadOnlySpan<T>.CastUp, and the span op_Implicit operators - so decompiled code showed the lowered form even though the conversion and betterness layers already implement the C# 14 rules. CallBuilder now folds those helper calls back into conversions, riding the existing mechanism: the conversion is built as an explicit cast, consumption sites make it implicit where the context allows, and the overload-resolution recheck re-adds a cast when the bare argument would bind to a different overload (which canonicalizes deliberate AsSpan disambiguations to the equivalent explicit span cast). Span conversions compose, so CastCanBeMadeImplicit lets a direct input-to-target span conversion replace a chained pair; and an rvalue bound to an in parameter gets the same chance to shed the cast as a by-value argument, since ChangeDirectionExpressionTo bypasses the by-value strip. Part of #829. Assisted-by: Claude:claude-fable-5:Claude Codepull/3930/head
9 changed files with 182 additions and 4 deletions
@ -0,0 +1,17 @@
@@ -0,0 +1,17 @@
|
||||
using System; |
||||
|
||||
namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty |
||||
{ |
||||
public class SpanConversionOperatorMismatch |
||||
{ |
||||
public static implicit operator ReadOnlySpan<char>(object o) |
||||
{ |
||||
return default(ReadOnlySpan<char>); |
||||
} |
||||
|
||||
public static ReadOnlySpan<char> ConvertString(string s) |
||||
{ |
||||
return (ReadOnlySpan<char>)(object)s; |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,57 @@
@@ -0,0 +1,57 @@
|
||||
// Regression fixture: an implicit span conversion must not be folded into a call to a conversion |
||||
// operator that does not perform it. |
||||
// |
||||
// C# cannot declare 'implicit operator ReadOnlySpan<char>(object)' - neither operand type is the |
||||
// declaring type - but IL can. An implicit span conversion from string to ReadOnlySpan<char> does |
||||
// exist, yet the compiler emits it as MemoryExtensions.AsSpan, not as this operator, so the call |
||||
// below is a user-defined conversion and the cast to its parameter type has to survive. |
||||
|
||||
.assembly extern System.Runtime |
||||
{ |
||||
.publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) |
||||
.ver 4:0:0:0 |
||||
} |
||||
.assembly SpanConversionOperatorMismatch |
||||
{ |
||||
.custom instance void [System.Runtime]System.Runtime.Versioning.TargetFrameworkAttribute::.ctor(string) = { |
||||
string('.NETCoreApp,Version=11.0') |
||||
} |
||||
.hash algorithm 0x00008004 |
||||
.ver 1:0:0:0 |
||||
} |
||||
.module SpanConversionOperatorMismatch.dll |
||||
|
||||
.class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.ILPretty.SpanConversionOperatorMismatch |
||||
extends [System.Runtime]System.Object |
||||
{ |
||||
.method public hidebysig specialname static |
||||
valuetype [System.Runtime]System.ReadOnlySpan`1<char> |
||||
op_Implicit(object o) cil managed |
||||
{ |
||||
.maxstack 1 |
||||
.locals init (valuetype [System.Runtime]System.ReadOnlySpan`1<char> V_0) |
||||
IL_0000: ldloca.s V_0 |
||||
IL_0002: initobj valuetype [System.Runtime]System.ReadOnlySpan`1<char> |
||||
IL_0008: ldloc.0 |
||||
IL_0009: ret |
||||
} |
||||
|
||||
.method public hidebysig static |
||||
valuetype [System.Runtime]System.ReadOnlySpan`1<char> |
||||
ConvertString(string s) cil managed |
||||
{ |
||||
.maxstack 8 |
||||
IL_0000: ldarg.0 |
||||
IL_0001: call valuetype [System.Runtime]System.ReadOnlySpan`1<char> ICSharpCode.Decompiler.Tests.TestCases.ILPretty.SpanConversionOperatorMismatch::op_Implicit(object) |
||||
IL_0006: ret |
||||
} |
||||
|
||||
.method public hidebysig specialname rtspecialname |
||||
instance void .ctor() cil managed |
||||
{ |
||||
.maxstack 8 |
||||
IL_0000: ldarg.0 |
||||
IL_0001: call instance void [System.Runtime]System.Object::.ctor() |
||||
IL_0006: ret |
||||
} |
||||
} |
||||
Loading…
Reference in new issue