Browse Source

Merge pull request #3588 from icsharpcode/r502

Roslyn 5.0.0
pull/3620/head
Christoph Wille 1 month ago committed by GitHub
parent
commit
f54955a185
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 4
      Directory.Packages.props
  2. 2
      ICSharpCode.Decompiler.Tests/Helpers/Tester.cs
  3. 2
      ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
  4. 2
      ICSharpCode.Decompiler/CSharp/CallBuilder.cs
  5. 174
      ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs
  6. 2
      ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs
  7. 4
      ICSharpCode.Decompiler/CSharp/Resolver/OverloadResolution.cs
  8. 66
      ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs
  9. 21
      ICSharpCode.Decompiler/DecompilerSettings.cs
  10. 22
      ICSharpCode.Decompiler/Semantics/Conversion.cs
  11. 26
      ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs
  12. 2
      ICSharpCode.Decompiler/TypeSystem/ICompilation.cs
  13. 2
      ICSharpCode.Decompiler/TypeSystem/Implementation/SimpleCompilation.cs
  14. 11
      ILSpy/Properties/Resources.Designer.cs
  15. 3
      ILSpy/Properties/Resources.resx

4
Directory.Packages.props

@ -14,8 +14,8 @@
<PackageVersion Include="K4os.Compression.LZ4" Version="1.3.8" /> <PackageVersion Include="K4os.Compression.LZ4" Version="1.3.8" />
<PackageVersion Include="McMaster.Extensions.CommandLineUtils" Version="4.1.1" /> <PackageVersion Include="McMaster.Extensions.CommandLineUtils" Version="4.1.1" />
<PackageVersion Include="McMaster.Extensions.Hosting.CommandLine" Version="4.1.1" /> <PackageVersion Include="McMaster.Extensions.Hosting.CommandLine" Version="4.1.1" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.14.0" /> <PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="5.0.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.VisualBasic" Version="4.14.0" /> <PackageVersion Include="Microsoft.CodeAnalysis.VisualBasic" Version="5.0.0" />
<PackageVersion Include="Microsoft.DiaSymReader.Converter.Xml" Version="1.1.0-beta2-22171-02" /> <PackageVersion Include="Microsoft.DiaSymReader.Converter.Xml" Version="1.1.0-beta2-22171-02" />
<PackageVersion Include="Microsoft.DiaSymReader" Version="1.4.0" /> <PackageVersion Include="Microsoft.DiaSymReader" Version="1.4.0" />
<PackageVersion Include="Microsoft.DiaSymReader.Native" Version="17.0.0-beta1.21524.1" /> <PackageVersion Include="Microsoft.DiaSymReader.Native" Version="17.0.0-beta1.21524.1" />

2
ICSharpCode.Decompiler.Tests/Helpers/Tester.cs

@ -721,7 +721,7 @@ namespace System.Runtime.CompilerServices
CompilerOptions.UseRoslyn1_3_2 => CSharp.LanguageVersion.CSharp6, CompilerOptions.UseRoslyn1_3_2 => CSharp.LanguageVersion.CSharp6,
CompilerOptions.UseRoslyn2_10_0 => CSharp.LanguageVersion.CSharp7_3, CompilerOptions.UseRoslyn2_10_0 => CSharp.LanguageVersion.CSharp7_3,
CompilerOptions.UseRoslyn3_11_0 => CSharp.LanguageVersion.CSharp9_0, CompilerOptions.UseRoslyn3_11_0 => CSharp.LanguageVersion.CSharp9_0,
_ => cscOptions.HasFlag(CompilerOptions.Preview) ? CSharp.LanguageVersion.Latest : CSharp.LanguageVersion.CSharp13_0, _ => cscOptions.HasFlag(CompilerOptions.Preview) ? CSharp.LanguageVersion.Latest : CSharp.LanguageVersion.CSharp14_0,
}; };
DecompilerSettings settings = new(langVersion) { DecompilerSettings settings = new(langVersion) {
// Never use file-scoped namespaces // Never use file-scoped namespaces

2
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

@ -562,7 +562,7 @@ namespace ICSharpCode.Decompiler.Tests
[Test] [Test]
public async Task ExtensionProperties([ValueSource(nameof(roslyn4OrNewerOptions))] CompilerOptions cscOptions) public async Task ExtensionProperties([ValueSource(nameof(roslyn4OrNewerOptions))] CompilerOptions cscOptions)
{ {
await RunForLibrary(cscOptions: cscOptions | CompilerOptions.Preview); await RunForLibrary(cscOptions: cscOptions | CompilerOptions.Preview | CompilerOptions.NullableEnable);
} }
[Test] [Test]

2
ICSharpCode.Decompiler/CSharp/CallBuilder.cs

@ -1457,7 +1457,7 @@ namespace ICSharpCode.Decompiler.CSharp
var conversions = CSharpConversions.Get(expressionBuilder.compilation); var conversions = CSharpConversions.Get(expressionBuilder.compilation);
IType targetType = method.ReturnType; IType targetType = method.ReturnType;
var conv = conversions.ImplicitConversion(argument.Type, targetType); var conv = conversions.ImplicitConversion(argument.Type, targetType);
if (!(conv.IsUserDefined && conv.IsValid && conv.Method.Equals(method))) if (!(conv.IsUserDefined && conv.IsValid && conv.Method.Equals(method, NormalizeTypeVisitor.TypeErasure)))
{ {
// implicit conversion to targetType isn't directly possible, so first insert a cast to the argument type // implicit conversion to targetType isn't directly possible, so first insert a cast to the argument type
argument = argument.ConvertTo(method.Parameters[0].Type, expressionBuilder); argument = argument.ConvertTo(method.Parameters[0].Type, expressionBuilder);

174
ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs

@ -247,6 +247,10 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
if (IdentityConversion(elementType, spanElementType)) if (IdentityConversion(elementType, spanElementType))
return Conversion.InlineArrayConversion; return Conversion.InlineArrayConversion;
} }
if (IsImplicitSpanConversion(fromType, toType))
{
return Conversion.ImplicitSpanConversion;
}
return Conversion.None; return Conversion.None;
} }
@ -1229,6 +1233,49 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
} }
#endregion #endregion
#region Implicit Span Conversion
bool IsImplicitSpanConversion(IType fromType, IType toType)
{
if (!compilation.TypeSystemOptions.HasFlag(TypeSystemOptions.FirstClassSpanTypes))
{
return false;
}
// An implicit span conversion permits array_types, System.Span<T>, System.ReadOnlySpan<T>,
// and string to be converted between each other
// see https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-14.0/first-class-span-types#span-conversions
switch (fromType)
{
case ArrayType { Dimensions: 1, ElementType: var elementType }:
if (toType.IsKnownType(KnownTypeCode.SpanOfT))
{
return IdentityConversion(elementType, toType.TypeArguments[0]);
}
if (toType.IsKnownType(KnownTypeCode.ReadOnlySpanOfT))
{
return IdentityConversion(elementType, toType.TypeArguments[0])
|| IsImplicitReferenceConversion(elementType, toType.TypeArguments[0]);
}
break;
case ParameterizedType pt when pt.IsKnownType(KnownTypeCode.SpanOfT) || pt.IsKnownType(KnownTypeCode.ReadOnlySpanOfT):
if (toType.IsKnownType(KnownTypeCode.ReadOnlySpanOfT))
{
return IdentityConversion(pt.TypeArguments[0], toType.TypeArguments[0])
|| IsImplicitReferenceConversion(pt.TypeArguments[0], toType.TypeArguments[0]);
}
break;
case var s when s.IsKnownType(KnownTypeCode.String):
return toType.IsKnownType(KnownTypeCode.ReadOnlySpanOfT)
&& toType.TypeArguments[0].IsKnownType(KnownTypeCode.Char);
}
return false;
}
#endregion
#region AnonymousFunctionConversion #region AnonymousFunctionConversion
Conversion AnonymousFunctionConversion(ResolveResult resolveResult, IType toType) Conversion AnonymousFunctionConversion(ResolveResult resolveResult, IType toType)
{ {
@ -1487,11 +1534,32 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
#region BetterConversion #region BetterConversion
/// <summary> /// <summary>
/// Gets the better conversion (C# 4.0 spec, §7.5.3.3) /// Gets the better conversion (from expression) (C# 8.0 spec, §12.6.4.5)
/// </summary> /// </summary>
/// <returns>0 = neither is better; 1 = t1 is better; 2 = t2 is better</returns> /// <returns>0 = neither is better; 1 = t1 is better; 2 = t2 is better</returns>
public int BetterConversion(ResolveResult resolveResult, IType t1, IType t2) public int BetterConversion(ResolveResult resolveResult, IType t1, IType t2)
{ {
bool t1Exact = IsExactlyMatching(resolveResult, t1);
bool t2Exact = IsExactlyMatching(resolveResult, t2);
if (t1Exact && !t2Exact)
return 1;
if (t2Exact && !t1Exact)
return 2;
if (!t1Exact && !t2Exact)
{
bool c1ImplicitSpanConversion = IsImplicitSpanConversion(resolveResult.Type, t1);
bool c2ImplicitSpanConversion = IsImplicitSpanConversion(resolveResult.Type, t2);
if (c1ImplicitSpanConversion && !c2ImplicitSpanConversion)
return 1;
if (c2ImplicitSpanConversion && !c1ImplicitSpanConversion)
return 2;
}
if (t1Exact == t2Exact)
{
int r = BetterConversionTarget(t1, t2);
if (r != 0)
return r;
}
LambdaResolveResult lambda = resolveResult as LambdaResolveResult; LambdaResolveResult lambda = resolveResult as LambdaResolveResult;
if (lambda != null) if (lambda != null)
{ {
@ -1542,20 +1610,56 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
} }
/// <summary> /// <summary>
/// Unpacks the generic Task[T]. Returns null if the input is not Task[T]. /// Gets whether an expression E exactly matches a type T (C# 8.0 spec, §12.6.4.6)
/// </summary> /// </summary>
static IType UnpackTask(IType type) bool IsExactlyMatching(ResolveResult e, IType t)
{ {
ParameterizedType pt = type as ParameterizedType; var s = e.Type;
if (pt != null && pt.TypeParameterCount == 1 && pt.Name == "Task" && pt.Namespace == "System.Threading.Tasks") if (IdentityConversion(s, t))
return true;
if (e is LambdaResolveResult lambda)
{ {
return pt.GetTypeArgument(0); if (!lambda.IsAnonymousMethod)
{
t = UnpackExpressionTreeType(t);
}
IMethod m = t.GetDelegateInvokeMethod();
if (m == null)
return false;
IType[] parameterTypes = new IType[m.Parameters.Count];
for (int i = 0; i < parameterTypes.Length; i++)
parameterTypes[i] = m.Parameters[i].Type;
var x = lambda.GetInferredReturnType(parameterTypes);
var y = m.ReturnType;
if (IdentityConversion(x, y))
return true;
if (lambda.IsAsync)
{
x = UnpackTask(x);
y = UnpackTask(y);
}
if (x != null && y != null)
return IsExactlyMatching(new ResolveResult(x), y);
return false;
}
else
{
return false;
} }
return null;
} }
/// <summary> /// <summary>
/// Gets the better conversion (C# 4.0 spec, §7.5.3.4) /// Unpacks the generic TaskType[T]. Returns null if the input is not TaskType[T].
/// </summary>
static IType UnpackTask(IType type)
{
return (TaskType.IsTask(type) || TaskType.IsCustomTask(type, out _)) && type.TypeParameterCount == 1
? type.TypeArguments[0]
: null;
}
/// <summary>
/// Gets the better conversion (from type) (C# 4.0 spec, §7.5.3.4)
/// </summary> /// </summary>
/// <returns>0 = neither is better; 1 = t1 is better; 2 = t2 is better</returns> /// <returns>0 = neither is better; 1 = t1 is better; 2 = t2 is better</returns>
public int BetterConversion(IType s, IType t1, IType t2) public int BetterConversion(IType s, IType t1, IType t2)
@ -1570,17 +1674,57 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
} }
/// <summary> /// <summary>
/// Gets the better conversion target (C# 4.0 spec, §7.5.3.5) /// Gets the better conversion target (C# 9.0 spec, §12.6.4.7)
/// </summary> /// </summary>
/// <returns>0 = neither is better; 1 = t1 is better; 2 = t2 is better</returns> /// <returns>0 = neither is better; 1 = t1 is better; 2 = t2 is better</returns>
int BetterConversionTarget(IType t1, IType t2) int BetterConversionTarget(IType t1, IType t2)
{ {
bool t1To2 = ImplicitConversion(t1, t2).IsValid; if (t1.IsKnownType(KnownTypeCode.ReadOnlySpanOfT))
bool t2To1 = ImplicitConversion(t2, t1).IsValid; {
if (t1To2 && !t2To1) if (t2.IsKnownType(KnownTypeCode.SpanOfT))
return 1; {
if (t2To1 && !t1To2) if (IdentityConversion(t1.TypeArguments[0], t2.TypeArguments[0]))
return 2; return 1;
}
if (t2.IsKnownType(KnownTypeCode.ReadOnlySpanOfT))
{
bool t1To2 = ImplicitConversion(t1.TypeArguments[0], t2.TypeArguments[0]).IsValid;
bool t2To1 = ImplicitConversion(t2.TypeArguments[0], t1.TypeArguments[0]).IsValid;
if (t1To2 && !t2To1)
return 1;
}
}
if (t2.IsKnownType(KnownTypeCode.ReadOnlySpanOfT))
{
if (t1.IsKnownType(KnownTypeCode.SpanOfT))
{
if (IdentityConversion(t2.TypeArguments[0], t1.TypeArguments[0]))
return 2;
}
if (t1.IsKnownType(KnownTypeCode.ReadOnlySpanOfT))
{
bool t1To2 = ImplicitConversion(t1.TypeArguments[0], t2.TypeArguments[0]).IsValid;
bool t2To1 = ImplicitConversion(t2.TypeArguments[0], t1.TypeArguments[0]).IsValid;
if (t2To1 && !t1To2)
return 2;
}
}
{
bool t1To2 = ImplicitConversion(t1, t2).IsValid;
bool t2To1 = ImplicitConversion(t2, t1).IsValid;
if (t1To2 && !t2To1)
return 1;
if (t2To1 && !t1To2)
return 2;
}
var s1 = UnpackTask(t1);
var s2 = UnpackTask(t2);
if (s1 != null && s2 != null)
return BetterConversionTarget(s1, s2);
TypeCode t1Code = ReflectionHelper.GetTypeCode(t1); TypeCode t1Code = ReflectionHelper.GetTypeCode(t1);
TypeCode t2Code = ReflectionHelper.GetTypeCode(t2); TypeCode t2Code = ReflectionHelper.GetTypeCode(t2);
if (IsBetterIntegralType(t1Code, t2Code)) if (IsBetterIntegralType(t1Code, t2Code))

2
ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs

@ -2171,7 +2171,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
thisParameterType = thisParameterType.AcceptVisitor(substitution); thisParameterType = thisParameterType.AcceptVisitor(substitution);
} }
Conversion c = conversions.ImplicitConversion(targetType, thisParameterType); Conversion c = conversions.ImplicitConversion(targetType, thisParameterType);
return c.IsValid && (c.IsIdentityConversion || c.IsReferenceConversion || c.IsBoxingConversion); return c.IsValid && (c.IsIdentityConversion || c.IsReferenceConversion || c.IsBoxingConversion || c.IsImplicitSpanConversion);
} }
/// <summary> /// <summary>

4
ICSharpCode.Decompiler/CSharp/Resolver/OverloadResolution.cs

@ -710,8 +710,8 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
candidate.ArgumentConversions[i] = c; candidate.ArgumentConversions[i] = c;
if (IsExtensionMethodInvocation && parameterIndex == 0) if (IsExtensionMethodInvocation && parameterIndex == 0)
{ {
// First parameter to extension method must be an identity, reference or boxing conversion // First parameter to extension method must be an identity, reference, boxing or span conversion
if (!(c == Conversion.IdentityConversion || c == Conversion.ImplicitReferenceConversion || c == Conversion.BoxingConversion)) if (!(c == Conversion.IdentityConversion || c == Conversion.ImplicitReferenceConversion || c == Conversion.BoxingConversion || c == Conversion.ImplicitSpanConversion))
candidate.AddError(OverloadResolutionErrors.ArgumentTypeMismatch); candidate.AddError(OverloadResolutionErrors.ArgumentTypeMismatch);
} }
else else

66
ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs

@ -659,17 +659,31 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
return; return;
} }
// Handle array types: // Handle array types:
ArrayType arrU = U as ArrayType; U = U.TupleUnderlyingTypeOrSelf();
ArrayType arrV = V as ArrayType; V = V.TupleUnderlyingTypeOrSelf();
if (arrU != null && arrV != null && arrU.Dimensions == arrV.Dimensions) switch ((U, V))
{ {
MakeExactInference(arrU.ElementType, arrV.ElementType); case (ArrayType arrU, ArrayType arrV) when arrU.Dimensions == arrV.Dimensions:
return; MakeExactInference(arrU.ElementType, arrV.ElementType);
return;
case (ArrayType arrU, ParameterizedType spanV) when compilation.TypeSystemOptions.HasFlag(TypeSystemOptions.FirstClassSpanTypes) && spanV.IsKnownType(KnownTypeCode.SpanOfT):
MakeExactInference(arrU.ElementType, spanV.TypeArguments[0]);
return;
case (ParameterizedType spanU, ParameterizedType spanV) when compilation.TypeSystemOptions.HasFlag(TypeSystemOptions.FirstClassSpanTypes) && spanU.IsKnownType(KnownTypeCode.SpanOfT) && spanV.IsKnownType(KnownTypeCode.SpanOfT):
MakeExactInference(spanU.TypeArguments[0], spanV.TypeArguments[0]);
return;
case (ArrayType arrU, ParameterizedType rosV) when compilation.TypeSystemOptions.HasFlag(TypeSystemOptions.FirstClassSpanTypes) && rosV.IsKnownType(KnownTypeCode.ReadOnlySpanOfT):
MakeExactInference(arrU.ElementType, rosV.TypeArguments[0]);
return;
case (ParameterizedType spanU, ParameterizedType rosV) when compilation.TypeSystemOptions.HasFlag(TypeSystemOptions.FirstClassSpanTypes) && spanU.IsKnownType(KnownTypeCode.SpanOfT) && rosV.IsKnownType(KnownTypeCode.ReadOnlySpanOfT):
MakeExactInference(spanU.TypeArguments[0], rosV.TypeArguments[0]);
return;
case (ParameterizedType rosU, ParameterizedType rosV) when compilation.TypeSystemOptions.HasFlag(TypeSystemOptions.FirstClassSpanTypes) && rosU.IsKnownType(KnownTypeCode.ReadOnlySpanOfT) && rosV.IsKnownType(KnownTypeCode.ReadOnlySpanOfT):
MakeExactInference(rosU.TypeArguments[0], rosV.TypeArguments[0]);
return;
} }
// Handle parameterized type: // Handle parameterized type:
ParameterizedType pU = U.TupleUnderlyingTypeOrSelf() as ParameterizedType; if (U is ParameterizedType pU && V is ParameterizedType pV
ParameterizedType pV = V.TupleUnderlyingTypeOrSelf() as ParameterizedType;
if (pU != null && pV != null
&& object.Equals(pU.GenericType, pV.GenericType) && object.Equals(pU.GenericType, pV.GenericType)
&& pU.TypeParameterCount == pV.TypeParameterCount) && pU.TypeParameterCount == pV.TypeParameterCount)
{ {
@ -751,21 +765,33 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
return; return;
} }
// Handle array types: // Handle array types:
ArrayType arrU = U as ArrayType; V = V.TupleUnderlyingTypeOrSelf();
ArrayType arrV = V as ArrayType; switch ((U, V))
ParameterizedType pV = V.TupleUnderlyingTypeOrSelf() as ParameterizedType;
if (arrU != null && arrV != null && arrU.Dimensions == arrV.Dimensions)
{ {
MakeLowerBoundInference(arrU.ElementType, arrV.ElementType); case (ArrayType arrU, ArrayType arrV) when arrU.Dimensions == arrV.Dimensions:
return; MakeLowerBoundInference(arrU.ElementType, arrV.ElementType);
} return;
else if (arrU != null && pV.IsArrayInterfaceType() && arrU.Dimensions == 1) case (ArrayType arrU, ParameterizedType spanV) when compilation.TypeSystemOptions.HasFlag(TypeSystemOptions.FirstClassSpanTypes) && spanV.IsKnownType(KnownTypeCode.SpanOfT):
{ MakeLowerBoundInference(arrU.ElementType, spanV.TypeArguments[0]);
MakeLowerBoundInference(arrU.ElementType, pV.GetTypeArgument(0)); return;
return; case (ParameterizedType spanU, ParameterizedType spanV) when compilation.TypeSystemOptions.HasFlag(TypeSystemOptions.FirstClassSpanTypes) && spanU.IsKnownType(KnownTypeCode.SpanOfT) && spanV.IsKnownType(KnownTypeCode.SpanOfT):
MakeLowerBoundInference(spanU.TypeArguments[0], spanV.TypeArguments[0]);
return;
case (ArrayType arrU, ParameterizedType rosV) when compilation.TypeSystemOptions.HasFlag(TypeSystemOptions.FirstClassSpanTypes) && rosV.IsKnownType(KnownTypeCode.ReadOnlySpanOfT):
MakeLowerBoundInference(arrU.ElementType, rosV.TypeArguments[0]);
return;
case (ParameterizedType spanU, ParameterizedType rosV) when compilation.TypeSystemOptions.HasFlag(TypeSystemOptions.FirstClassSpanTypes) && spanU.IsKnownType(KnownTypeCode.SpanOfT) && rosV.IsKnownType(KnownTypeCode.ReadOnlySpanOfT):
MakeLowerBoundInference(spanU.TypeArguments[0], rosV.TypeArguments[0]);
return;
case (ParameterizedType rosU, ParameterizedType rosV) when compilation.TypeSystemOptions.HasFlag(TypeSystemOptions.FirstClassSpanTypes) && rosU.IsKnownType(KnownTypeCode.ReadOnlySpanOfT) && rosV.IsKnownType(KnownTypeCode.ReadOnlySpanOfT):
MakeLowerBoundInference(rosU.TypeArguments[0], rosV.TypeArguments[0]);
return;
case (ArrayType arrU, ParameterizedType arrIntfV) when arrIntfV.IsArrayInterfaceType() && arrU.Dimensions == 1:
MakeLowerBoundInference(arrU.ElementType, arrIntfV.TypeArguments[0]);
return;
} }
// Handle parameterized types: // Handle parameterized types:
if (pV != null) if (V is ParameterizedType pV)
{ {
ParameterizedType uniqueBaseType = null; ParameterizedType uniqueBaseType = null;
foreach (IType baseU in U.GetAllBaseTypes()) foreach (IType baseU in U.GetAllBaseTypes())

21
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -175,12 +175,13 @@ namespace ICSharpCode.Decompiler
if (languageVersion < CSharp.LanguageVersion.CSharp14_0) if (languageVersion < CSharp.LanguageVersion.CSharp14_0)
{ {
extensionMembers = false; extensionMembers = false;
firstClassSpanTypes = false;
} }
} }
public CSharp.LanguageVersion GetMinimumRequiredVersion() public CSharp.LanguageVersion GetMinimumRequiredVersion()
{ {
if (extensionMembers) if (extensionMembers || firstClassSpanTypes)
return CSharp.LanguageVersion.CSharp14_0; return CSharp.LanguageVersion.CSharp14_0;
if (paramsCollections) if (paramsCollections)
return CSharp.LanguageVersion.CSharp13_0; return CSharp.LanguageVersion.CSharp13_0;
@ -2179,6 +2180,24 @@ namespace ICSharpCode.Decompiler
} }
} }
bool firstClassSpanTypes = true;
/// <summary>
/// Gets/Sets whether (ReadOnly)Span&lt;T&gt; should be treated like built-in types.
/// </summary>
[Category("C# 14.0 / VS 202x.yy")]
[Description("DecompilerSettings.FirstClassSpanTypes")]
public bool FirstClassSpanTypes {
get { return firstClassSpanTypes; }
set {
if (firstClassSpanTypes != value)
{
firstClassSpanTypes = value;
OnPropertyChanged();
}
}
}
bool separateLocalVariableDeclarations = false; bool separateLocalVariableDeclarations = false;
/// <summary> /// <summary>

22
ICSharpCode.Decompiler/Semantics/Conversion.cs

@ -92,6 +92,11 @@ namespace ICSharpCode.Decompiler.Semantics
/// </summary> /// </summary>
public static readonly Conversion InlineArrayConversion = new BuiltinConversion(true, 12); public static readonly Conversion InlineArrayConversion = new BuiltinConversion(true, 12);
/// <summary>
/// C# 14 implicit span conversion from an array type to <see cref="System.Span{T}"/> or <see cref="System.ReadOnlySpan{T}"/>.
/// </summary>
public static readonly Conversion ImplicitSpanConversion = new BuiltinConversion(true, 13);
public static Conversion UserDefinedConversion(IMethod operatorMethod, bool isImplicit, Conversion conversionBeforeUserDefinedOperator, Conversion conversionAfterUserDefinedOperator, bool isLifted = false, bool isAmbiguous = false) public static Conversion UserDefinedConversion(IMethod operatorMethod, bool isImplicit, Conversion conversionBeforeUserDefinedOperator, Conversion conversionAfterUserDefinedOperator, bool isLifted = false, bool isAmbiguous = false)
{ {
if (operatorMethod == null) if (operatorMethod == null)
@ -250,6 +255,9 @@ namespace ICSharpCode.Decompiler.Semantics
get { return type == 11; } get { return type == 11; }
} }
public override bool IsInlineArrayConversion => type == 12;
public override bool IsImplicitSpanConversion => type == 13;
public override string ToString() public override string ToString()
{ {
string name = null; string name = null;
@ -284,6 +292,10 @@ namespace ICSharpCode.Decompiler.Semantics
return "interpolated string"; return "interpolated string";
case 11: case 11:
return "throw-expression conversion"; return "throw-expression conversion";
case 12:
return "inline array conversion";
case 13:
return "implicit span conversion";
} }
return (isImplicit ? "implicit " : "explicit ") + name + " conversion"; return (isImplicit ? "implicit " : "explicit ") + name + " conversion";
} }
@ -621,6 +633,16 @@ namespace ICSharpCode.Decompiler.Semantics
/// </summary> /// </summary>
public virtual bool IsInterpolatedStringConversion => false; public virtual bool IsInterpolatedStringConversion => false;
/// <summary>
/// Gets whether this is an inline array conversion to <see cref="System.Span{T}"/> or <see cref="System.ReadOnlySpan{T}"/>.
/// </summary>
public virtual bool IsInlineArrayConversion => false;
/// <summary>
/// Gets whether this is an implicit span conversion from an array type to <see cref="System.Span{T}"/> or <see cref="System.ReadOnlySpan{T}"/>.
/// </summary>
public virtual bool IsImplicitSpanConversion => false;
/// <summary> /// <summary>
/// For a tuple conversion, gets the individual tuple element conversions. /// For a tuple conversion, gets the individual tuple element conversions.
/// </summary> /// </summary>

26
ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs

@ -145,12 +145,17 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// </summary> /// </summary>
ParamsCollections = 0x20000, ParamsCollections = 0x20000,
/// <summary> /// <summary>
/// Default settings: typical options for the decompiler, with all C# languages features enabled. /// If this option is active, span types (Span&lt;T&gt; and ReadOnlySpan&lt;T&gt;) are treated like
/// built-in types and language rules of C# 14 and later are applied.
/// </summary>
FirstClassSpanTypes = 0x40000,
/// <summary>
/// Default settings: typical options for the decompiler, with all C# language features enabled.
/// </summary> /// </summary>
Default = Dynamic | Tuple | ExtensionMethods | DecimalConstants | ReadOnlyStructsAndParameters Default = Dynamic | Tuple | ExtensionMethods | DecimalConstants | ReadOnlyStructsAndParameters
| RefStructs | UnmanagedConstraints | NullabilityAnnotations | ReadOnlyMethods | RefStructs | UnmanagedConstraints | NullabilityAnnotations | ReadOnlyMethods
| NativeIntegers | FunctionPointers | ScopedRef | NativeIntegersWithoutAttribute | NativeIntegers | FunctionPointers | ScopedRef | NativeIntegersWithoutAttribute
| RefReadOnlyParameters | ParamsCollections | RefReadOnlyParameters | ParamsCollections | FirstClassSpanTypes
} }
/// <summary> /// <summary>
@ -194,6 +199,8 @@ namespace ICSharpCode.Decompiler.TypeSystem
typeSystemOptions |= TypeSystemOptions.RefReadOnlyParameters; typeSystemOptions |= TypeSystemOptions.RefReadOnlyParameters;
if (settings.ParamsCollections) if (settings.ParamsCollections)
typeSystemOptions |= TypeSystemOptions.ParamsCollections; typeSystemOptions |= TypeSystemOptions.ParamsCollections;
if (settings.FirstClassSpanTypes)
typeSystemOptions |= TypeSystemOptions.FirstClassSpanTypes;
return typeSystemOptions; return typeSystemOptions;
} }
@ -213,16 +220,18 @@ namespace ICSharpCode.Decompiler.TypeSystem
throw new ArgumentNullException(nameof(mainModule)); throw new ArgumentNullException(nameof(mainModule));
if (assemblyResolver == null) if (assemblyResolver == null)
throw new ArgumentNullException(nameof(assemblyResolver)); throw new ArgumentNullException(nameof(assemblyResolver));
var ts = new DecompilerTypeSystem(); var ts = new DecompilerTypeSystem(typeSystemOptions);
await ts.InitializeAsync(mainModule, assemblyResolver, typeSystemOptions) await ts.InitializeAsync(mainModule, assemblyResolver)
.ConfigureAwait(false); .ConfigureAwait(false);
return ts; return ts;
} }
private MetadataModule mainModule; private MetadataModule mainModule;
private TypeSystemOptions typeSystemOptions;
private DecompilerTypeSystem() private DecompilerTypeSystem(TypeSystemOptions typeSystemOptions)
{ {
this.typeSystemOptions = typeSystemOptions;
} }
public DecompilerTypeSystem(MetadataFile mainModule, IAssemblyResolver assemblyResolver) public DecompilerTypeSystem(MetadataFile mainModule, IAssemblyResolver assemblyResolver)
@ -236,12 +245,13 @@ namespace ICSharpCode.Decompiler.TypeSystem
} }
public DecompilerTypeSystem(MetadataFile mainModule, IAssemblyResolver assemblyResolver, TypeSystemOptions typeSystemOptions) public DecompilerTypeSystem(MetadataFile mainModule, IAssemblyResolver assemblyResolver, TypeSystemOptions typeSystemOptions)
: this(typeSystemOptions)
{ {
if (mainModule == null) if (mainModule == null)
throw new ArgumentNullException(nameof(mainModule)); throw new ArgumentNullException(nameof(mainModule));
if (assemblyResolver == null) if (assemblyResolver == null)
throw new ArgumentNullException(nameof(assemblyResolver)); throw new ArgumentNullException(nameof(assemblyResolver));
InitializeAsync(mainModule, assemblyResolver, typeSystemOptions).GetAwaiter().GetResult(); InitializeAsync(mainModule, assemblyResolver).GetAwaiter().GetResult();
} }
static readonly string[] implicitReferences = new[] { static readonly string[] implicitReferences = new[] {
@ -249,7 +259,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
"System.Runtime.CompilerServices.Unsafe" "System.Runtime.CompilerServices.Unsafe"
}; };
private async Task InitializeAsync(MetadataFile mainModule, IAssemblyResolver assemblyResolver, TypeSystemOptions typeSystemOptions) private async Task InitializeAsync(MetadataFile mainModule, IAssemblyResolver assemblyResolver)
{ {
// Load referenced assemblies and type-forwarder references. // Load referenced assemblies and type-forwarder references.
// This is necessary to make .NET Core/PCL binaries work better. // This is necessary to make .NET Core/PCL binaries work better.
@ -410,5 +420,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
} }
public new MetadataModule MainModule => mainModule; public new MetadataModule MainModule => mainModule;
public override TypeSystemOptions TypeSystemOptions => typeSystemOptions;
} }
} }

2
ICSharpCode.Decompiler/TypeSystem/ICompilation.cs

@ -76,6 +76,8 @@ namespace ICSharpCode.Decompiler.TypeSystem
StringComparer NameComparer { get; } StringComparer NameComparer { get; }
CacheManager CacheManager { get; } CacheManager CacheManager { get; }
TypeSystemOptions TypeSystemOptions { get; }
} }
public interface ICompilationProvider public interface ICompilationProvider

2
ICSharpCode.Decompiler/TypeSystem/Implementation/SimpleCompilation.cs

@ -157,6 +157,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
get { return StringComparer.Ordinal; } get { return StringComparer.Ordinal; }
} }
public virtual TypeSystemOptions TypeSystemOptions => TypeSystemOptions.Default;
public override string ToString() public override string ToString()
{ {
return "[" + GetType().Name + " " + mainModule.AssemblyName + "]"; return "[" + GetType().Name + " " + mainModule.AssemblyName + "]";

11
ILSpy/Properties/Resources.Designer.cs generated

@ -19,7 +19,7 @@ namespace ICSharpCode.ILSpy.Properties {
// class via a tool like ResGen or Visual Studio. // class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen // To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project. // with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "18.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class Resources { public class Resources {
@ -1053,6 +1053,15 @@ namespace ICSharpCode.ILSpy.Properties {
} }
} }
/// <summary>
/// Looks up a localized string similar to Treat Span&lt;T&gt; and ReadOnlySpan&lt;T&gt; as built-in types.
/// </summary>
public static string DecompilerSettings_FirstClassSpanTypes {
get {
return ResourceManager.GetString("DecompilerSettings.FirstClassSpanTypes", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Transform to for, if possible. /// Looks up a localized string similar to Transform to for, if possible.
/// </summary> /// </summary>

3
ILSpy/Properties/Resources.resx

@ -375,6 +375,9 @@ Are you sure you want to continue?</value>
<data name="DecompilerSettings.FileScopedNamespaces" xml:space="preserve"> <data name="DecompilerSettings.FileScopedNamespaces" xml:space="preserve">
<value>Use file-scoped namespace declarations</value> <value>Use file-scoped namespace declarations</value>
</data> </data>
<data name="DecompilerSettings.FirstClassSpanTypes" xml:space="preserve">
<value>Treat Span&lt;T&gt; and ReadOnlySpan&lt;T&gt; as built-in types</value>
</data>
<data name="DecompilerSettings.ForStatement" xml:space="preserve"> <data name="DecompilerSettings.ForStatement" xml:space="preserve">
<value>Transform to for, if possible</value> <value>Transform to for, if possible</value>
</data> </data>

Loading…
Cancel
Save