Browse Source

Add support for C# 14 first-class span types in the type system.

pull/3588/head
Siegfried Pammer 1 month ago
parent
commit
396b58031b
  1. 2
      ICSharpCode.Decompiler.Tests/Helpers/Tester.cs
  2. 2
      ICSharpCode.Decompiler/CSharp/CallBuilder.cs
  3. 174
      ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs
  4. 2
      ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs
  5. 4
      ICSharpCode.Decompiler/CSharp/Resolver/OverloadResolution.cs
  6. 66
      ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs
  7. 21
      ICSharpCode.Decompiler/DecompilerSettings.cs
  8. 22
      ICSharpCode.Decompiler/Semantics/Conversion.cs
  9. 26
      ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs
  10. 2
      ICSharpCode.Decompiler/TypeSystem/ICompilation.cs
  11. 2
      ICSharpCode.Decompiler/TypeSystem/Implementation/SimpleCompilation.cs
  12. 11
      ILSpy/Properties/Resources.Designer.cs
  13. 3
      ILSpy/Properties/Resources.resx

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

@ -721,7 +721,7 @@ namespace System.Runtime.CompilerServices @@ -721,7 +721,7 @@ namespace System.Runtime.CompilerServices
CompilerOptions.UseRoslyn1_3_2 => CSharp.LanguageVersion.CSharp6,
CompilerOptions.UseRoslyn2_10_0 => CSharp.LanguageVersion.CSharp7_3,
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) {
// Never use file-scoped namespaces

2
ICSharpCode.Decompiler/CSharp/CallBuilder.cs

@ -1438,7 +1438,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1438,7 +1438,7 @@ namespace ICSharpCode.Decompiler.CSharp
var conversions = CSharpConversions.Get(expressionBuilder.compilation);
IType targetType = method.ReturnType;
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
argument = argument.ConvertTo(method.Parameters[0].Type, expressionBuilder);

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

@ -247,6 +247,10 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -247,6 +247,10 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
if (IdentityConversion(elementType, spanElementType))
return Conversion.InlineArrayConversion;
}
if (IsImplicitSpanConversion(fromType, toType))
{
return Conversion.ImplicitSpanConversion;
}
return Conversion.None;
}
@ -1229,6 +1233,49 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -1229,6 +1233,49 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
}
#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
Conversion AnonymousFunctionConversion(ResolveResult resolveResult, IType toType)
{
@ -1487,11 +1534,32 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -1487,11 +1534,32 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
#region BetterConversion
/// <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>
/// <returns>0 = neither is better; 1 = t1 is better; 2 = t2 is better</returns>
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;
if (lambda != null)
{
@ -1542,20 +1610,56 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -1542,20 +1610,56 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
}
/// <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>
static IType UnpackTask(IType type)
bool IsExactlyMatching(ResolveResult e, IType t)
{
ParameterizedType pt = type as ParameterizedType;
if (pt != null && pt.TypeParameterCount == 1 && pt.Name == "Task" && pt.Namespace == "System.Threading.Tasks")
var s = e.Type;
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>
/// 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>
/// <returns>0 = neither is better; 1 = t1 is better; 2 = t2 is better</returns>
public int BetterConversion(IType s, IType t1, IType t2)
@ -1570,17 +1674,57 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -1570,17 +1674,57 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
}
/// <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>
/// <returns>0 = neither is better; 1 = t1 is better; 2 = t2 is better</returns>
int BetterConversionTarget(IType t1, IType t2)
{
bool t1To2 = ImplicitConversion(t1, t2).IsValid;
bool t2To1 = ImplicitConversion(t2, t1).IsValid;
if (t1To2 && !t2To1)
return 1;
if (t2To1 && !t1To2)
return 2;
if (t1.IsKnownType(KnownTypeCode.ReadOnlySpanOfT))
{
if (t2.IsKnownType(KnownTypeCode.SpanOfT))
{
if (IdentityConversion(t1.TypeArguments[0], t2.TypeArguments[0]))
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 t2Code = ReflectionHelper.GetTypeCode(t2);
if (IsBetterIntegralType(t1Code, t2Code))

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

@ -2171,7 +2171,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -2171,7 +2171,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
thisParameterType = thisParameterType.AcceptVisitor(substitution);
}
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>

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

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

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

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

21
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -175,12 +175,13 @@ namespace ICSharpCode.Decompiler @@ -175,12 +175,13 @@ namespace ICSharpCode.Decompiler
if (languageVersion < CSharp.LanguageVersion.CSharp14_0)
{
extensionMembers = false;
firstClassSpanTypes = false;
}
}
public CSharp.LanguageVersion GetMinimumRequiredVersion()
{
if (extensionMembers)
if (extensionMembers || firstClassSpanTypes)
return CSharp.LanguageVersion.CSharp14_0;
if (paramsCollections)
return CSharp.LanguageVersion.CSharp13_0;
@ -2179,6 +2180,24 @@ namespace ICSharpCode.Decompiler @@ -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;
/// <summary>

22
ICSharpCode.Decompiler/Semantics/Conversion.cs

@ -92,6 +92,11 @@ namespace ICSharpCode.Decompiler.Semantics @@ -92,6 +92,11 @@ namespace ICSharpCode.Decompiler.Semantics
/// </summary>
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)
{
if (operatorMethod == null)
@ -250,6 +255,9 @@ namespace ICSharpCode.Decompiler.Semantics @@ -250,6 +255,9 @@ namespace ICSharpCode.Decompiler.Semantics
get { return type == 11; }
}
public override bool IsInlineArrayConversion => type == 12;
public override bool IsImplicitSpanConversion => type == 13;
public override string ToString()
{
string name = null;
@ -284,6 +292,10 @@ namespace ICSharpCode.Decompiler.Semantics @@ -284,6 +292,10 @@ namespace ICSharpCode.Decompiler.Semantics
return "interpolated string";
case 11:
return "throw-expression conversion";
case 12:
return "inline array conversion";
case 13:
return "implicit span conversion";
}
return (isImplicit ? "implicit " : "explicit ") + name + " conversion";
}
@ -621,6 +633,16 @@ namespace ICSharpCode.Decompiler.Semantics @@ -621,6 +633,16 @@ namespace ICSharpCode.Decompiler.Semantics
/// </summary>
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>
/// For a tuple conversion, gets the individual tuple element conversions.
/// </summary>

26
ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs

@ -145,12 +145,17 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -145,12 +145,17 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// </summary>
ParamsCollections = 0x20000,
/// <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>
Default = Dynamic | Tuple | ExtensionMethods | DecimalConstants | ReadOnlyStructsAndParameters
| RefStructs | UnmanagedConstraints | NullabilityAnnotations | ReadOnlyMethods
| NativeIntegers | FunctionPointers | ScopedRef | NativeIntegersWithoutAttribute
| RefReadOnlyParameters | ParamsCollections
| RefReadOnlyParameters | ParamsCollections | FirstClassSpanTypes
}
/// <summary>
@ -194,6 +199,8 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -194,6 +199,8 @@ namespace ICSharpCode.Decompiler.TypeSystem
typeSystemOptions |= TypeSystemOptions.RefReadOnlyParameters;
if (settings.ParamsCollections)
typeSystemOptions |= TypeSystemOptions.ParamsCollections;
if (settings.FirstClassSpanTypes)
typeSystemOptions |= TypeSystemOptions.FirstClassSpanTypes;
return typeSystemOptions;
}
@ -213,16 +220,18 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -213,16 +220,18 @@ namespace ICSharpCode.Decompiler.TypeSystem
throw new ArgumentNullException(nameof(mainModule));
if (assemblyResolver == null)
throw new ArgumentNullException(nameof(assemblyResolver));
var ts = new DecompilerTypeSystem();
await ts.InitializeAsync(mainModule, assemblyResolver, typeSystemOptions)
var ts = new DecompilerTypeSystem(typeSystemOptions);
await ts.InitializeAsync(mainModule, assemblyResolver)
.ConfigureAwait(false);
return ts;
}
private MetadataModule mainModule;
private TypeSystemOptions typeSystemOptions;
private DecompilerTypeSystem()
private DecompilerTypeSystem(TypeSystemOptions typeSystemOptions)
{
this.typeSystemOptions = typeSystemOptions;
}
public DecompilerTypeSystem(MetadataFile mainModule, IAssemblyResolver assemblyResolver)
@ -236,12 +245,13 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -236,12 +245,13 @@ namespace ICSharpCode.Decompiler.TypeSystem
}
public DecompilerTypeSystem(MetadataFile mainModule, IAssemblyResolver assemblyResolver, TypeSystemOptions typeSystemOptions)
: this(typeSystemOptions)
{
if (mainModule == null)
throw new ArgumentNullException(nameof(mainModule));
if (assemblyResolver == null)
throw new ArgumentNullException(nameof(assemblyResolver));
InitializeAsync(mainModule, assemblyResolver, typeSystemOptions).GetAwaiter().GetResult();
InitializeAsync(mainModule, assemblyResolver).GetAwaiter().GetResult();
}
static readonly string[] implicitReferences = new[] {
@ -249,7 +259,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -249,7 +259,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
"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.
// This is necessary to make .NET Core/PCL binaries work better.
@ -410,5 +420,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -410,5 +420,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
}
public new MetadataModule MainModule => mainModule;
public override TypeSystemOptions TypeSystemOptions => typeSystemOptions;
}
}

2
ICSharpCode.Decompiler/TypeSystem/ICompilation.cs

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

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

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

11
ILSpy/Properties/Resources.Designer.cs generated

@ -19,7 +19,7 @@ namespace ICSharpCode.ILSpy.Properties { @@ -19,7 +19,7 @@ namespace ICSharpCode.ILSpy.Properties {
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// 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.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class Resources {
@ -1053,6 +1053,15 @@ namespace ICSharpCode.ILSpy.Properties { @@ -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>
/// Looks up a localized string similar to Transform to for, if possible.
/// </summary>

3
ILSpy/Properties/Resources.resx

@ -375,6 +375,9 @@ Are you sure you want to continue?</value> @@ -375,6 +375,9 @@ Are you sure you want to continue?</value>
<data name="DecompilerSettings.FileScopedNamespaces" xml:space="preserve">
<value>Use file-scoped namespace declarations</value>
</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">
<value>Transform to for, if possible</value>
</data>

Loading…
Cancel
Save