diff --git a/Directory.Packages.props b/Directory.Packages.props
index 0341b7e9e..64faa2284 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -14,8 +14,8 @@
-
-
+
+
diff --git a/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs b/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs
index 6a9d9a06c..7317827da 100644
--- a/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs
+++ b/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs
@@ -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
diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
index d4b62f34d..15dc35b8a 100644
--- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
+++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
@@ -562,7 +562,7 @@ namespace ICSharpCode.Decompiler.Tests
[Test]
public async Task ExtensionProperties([ValueSource(nameof(roslyn4OrNewerOptions))] CompilerOptions cscOptions)
{
- await RunForLibrary(cscOptions: cscOptions | CompilerOptions.Preview);
+ await RunForLibrary(cscOptions: cscOptions | CompilerOptions.Preview | CompilerOptions.NullableEnable);
}
[Test]
diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs
index e822cf700..82977db20 100644
--- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs
+++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs
@@ -1457,7 +1457,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);
diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs
index 056ef2d54..044c61487 100644
--- a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs
+++ b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs
@@ -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
}
#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, System.ReadOnlySpan,
+ // 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
#region BetterConversion
///
- /// 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)
///
/// 0 = neither is better; 1 = t1 is better; 2 = t2 is better
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
}
///
- /// 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)
///
- 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;
}
///
- /// 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].
+ ///
+ static IType UnpackTask(IType type)
+ {
+ return (TaskType.IsTask(type) || TaskType.IsCustomTask(type, out _)) && type.TypeParameterCount == 1
+ ? type.TypeArguments[0]
+ : null;
+ }
+
+ ///
+ /// Gets the better conversion (from type) (C# 4.0 spec, §7.5.3.4)
///
/// 0 = neither is better; 1 = t1 is better; 2 = t2 is better
public int BetterConversion(IType s, IType t1, IType t2)
@@ -1570,17 +1674,57 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
}
///
- /// 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)
///
/// 0 = neither is better; 1 = t1 is better; 2 = t2 is better
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))
diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs
index 1f69df969..d3a49fbc9 100644
--- a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs
+++ b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs
@@ -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);
}
///
diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/OverloadResolution.cs b/ICSharpCode.Decompiler/CSharp/Resolver/OverloadResolution.cs
index 7e562179c..8e77d3133 100644
--- a/ICSharpCode.Decompiler/CSharp/Resolver/OverloadResolution.cs
+++ b/ICSharpCode.Decompiler/CSharp/Resolver/OverloadResolution.cs
@@ -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
diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs b/ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs
index 9ce37eff1..f90d45200 100644
--- a/ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs
+++ b/ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs
@@ -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
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())
diff --git a/ICSharpCode.Decompiler/DecompilerSettings.cs b/ICSharpCode.Decompiler/DecompilerSettings.cs
index 79055229d..8853717ab 100644
--- a/ICSharpCode.Decompiler/DecompilerSettings.cs
+++ b/ICSharpCode.Decompiler/DecompilerSettings.cs
@@ -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
}
}
+ bool firstClassSpanTypes = true;
+
+ ///
+ /// Gets/Sets whether (ReadOnly)Span<T> should be treated like built-in types.
+ ///
+ [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;
///
diff --git a/ICSharpCode.Decompiler/Semantics/Conversion.cs b/ICSharpCode.Decompiler/Semantics/Conversion.cs
index e4adbe746..fe94ce022 100644
--- a/ICSharpCode.Decompiler/Semantics/Conversion.cs
+++ b/ICSharpCode.Decompiler/Semantics/Conversion.cs
@@ -92,6 +92,11 @@ namespace ICSharpCode.Decompiler.Semantics
///
public static readonly Conversion InlineArrayConversion = new BuiltinConversion(true, 12);
+ ///
+ /// C# 14 implicit span conversion from an array type to or .
+ ///
+ 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
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
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
///
public virtual bool IsInterpolatedStringConversion => false;
+ ///
+ /// Gets whether this is an inline array conversion to or .
+ ///
+ public virtual bool IsInlineArrayConversion => false;
+
+ ///
+ /// Gets whether this is an implicit span conversion from an array type to or .
+ ///
+ public virtual bool IsImplicitSpanConversion => false;
+
///
/// For a tuple conversion, gets the individual tuple element conversions.
///
diff --git a/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs b/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs
index bc97dc56a..697f64b9d 100644
--- a/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs
+++ b/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs
@@ -145,12 +145,17 @@ namespace ICSharpCode.Decompiler.TypeSystem
///
ParamsCollections = 0x20000,
///
- /// Default settings: typical options for the decompiler, with all C# languages features enabled.
+ /// If this option is active, span types (Span<T> and ReadOnlySpan<T>) are treated like
+ /// built-in types and language rules of C# 14 and later are applied.
+ ///
+ FirstClassSpanTypes = 0x40000,
+ ///
+ /// Default settings: typical options for the decompiler, with all C# language features enabled.
///
Default = Dynamic | Tuple | ExtensionMethods | DecimalConstants | ReadOnlyStructsAndParameters
| RefStructs | UnmanagedConstraints | NullabilityAnnotations | ReadOnlyMethods
| NativeIntegers | FunctionPointers | ScopedRef | NativeIntegersWithoutAttribute
- | RefReadOnlyParameters | ParamsCollections
+ | RefReadOnlyParameters | ParamsCollections | FirstClassSpanTypes
}
///
@@ -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
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
}
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
"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
}
public new MetadataModule MainModule => mainModule;
+
+ public override TypeSystemOptions TypeSystemOptions => typeSystemOptions;
}
}
diff --git a/ICSharpCode.Decompiler/TypeSystem/ICompilation.cs b/ICSharpCode.Decompiler/TypeSystem/ICompilation.cs
index d350f33b3..344e92a58 100644
--- a/ICSharpCode.Decompiler/TypeSystem/ICompilation.cs
+++ b/ICSharpCode.Decompiler/TypeSystem/ICompilation.cs
@@ -76,6 +76,8 @@ namespace ICSharpCode.Decompiler.TypeSystem
StringComparer NameComparer { get; }
CacheManager CacheManager { get; }
+
+ TypeSystemOptions TypeSystemOptions { get; }
}
public interface ICompilationProvider
diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/SimpleCompilation.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/SimpleCompilation.cs
index fd7a217a2..0a30f0273 100644
--- a/ICSharpCode.Decompiler/TypeSystem/Implementation/SimpleCompilation.cs
+++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/SimpleCompilation.cs
@@ -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 + "]";
diff --git a/ILSpy/Properties/Resources.Designer.cs b/ILSpy/Properties/Resources.Designer.cs
index 9b6b5b0d5..c1b7320bd 100644
--- a/ILSpy/Properties/Resources.Designer.cs
+++ b/ILSpy/Properties/Resources.Designer.cs
@@ -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 {
}
}
+ ///
+ /// Looks up a localized string similar to Treat Span<T> and ReadOnlySpan<T> as built-in types.
+ ///
+ public static string DecompilerSettings_FirstClassSpanTypes {
+ get {
+ return ResourceManager.GetString("DecompilerSettings.FirstClassSpanTypes", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Transform to for, if possible.
///
diff --git a/ILSpy/Properties/Resources.resx b/ILSpy/Properties/Resources.resx
index 3ce2becb8..2a01be143 100644
--- a/ILSpy/Properties/Resources.resx
+++ b/ILSpy/Properties/Resources.resx
@@ -375,6 +375,9 @@ Are you sure you want to continue?
Use file-scoped namespace declarations
+
+ Treat Span<T> and ReadOnlySpan<T> as built-in types
+
Transform to for, if possible