Browse Source

Merge latest master with firstClassSpanTypes feature

Co-authored-by: christophwille <344208+christophwille@users.noreply.github.com>
copilot/add-field-keyword-auto-properties
copilot-swe-agent[bot] 1 month ago
parent
commit
382ee0fcad
  1. 4
      Directory.Packages.props
  2. 2
      ICSharpCode.Decompiler.Tests/Helpers/Tester.cs
  3. 2
      ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
  4. 28
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/NamedArguments.cs
  5. 2
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  6. 74
      ICSharpCode.Decompiler/CSharp/CallBuilder.cs
  7. 174
      ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs
  8. 2
      ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs
  9. 4
      ICSharpCode.Decompiler/CSharp/Resolver/OverloadResolution.cs
  10. 66
      ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs
  11. 21
      ICSharpCode.Decompiler/DecompilerSettings.cs
  12. 22
      ICSharpCode.Decompiler/Semantics/Conversion.cs
  13. 26
      ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs
  14. 2
      ICSharpCode.Decompiler/TypeSystem/ICompilation.cs
  15. 2
      ICSharpCode.Decompiler/TypeSystem/Implementation/SimpleCompilation.cs
  16. 11
      ILSpy/Properties/Resources.Designer.cs
  17. 3
      ILSpy/Properties/Resources.resx

4
Directory.Packages.props

@ -14,8 +14,8 @@ @@ -14,8 +14,8 @@
<PackageVersion Include="K4os.Compression.LZ4" Version="1.3.8" />
<PackageVersion Include="McMaster.Extensions.CommandLineUtils" 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.VisualBasic" Version="4.14.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="5.0.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" Version="1.4.0" />
<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 @@ -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.Tests/PrettyTestRunner.cs

@ -562,7 +562,7 @@ namespace ICSharpCode.Decompiler.Tests @@ -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]

28
ICSharpCode.Decompiler.Tests/TestCases/Pretty/NamedArguments.cs

@ -32,6 +32,34 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -32,6 +32,34 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
}
private class MustNotUseNamedArgsInCtor
{
public MustNotUseNamedArgsInCtor(string start = "", bool enable = false)
{
}
public MustNotUseNamedArgsInCtor(bool enable, string start = "")
{
}
public static MustNotUseNamedArgsInCtor Use()
{
// second overload
MustNotUseNamedArgsInCall(true);
// first overload
MustNotUseNamedArgsInCall();
return new MustNotUseNamedArgsInCtor(true);
}
public static void MustNotUseNamedArgsInCall(string start = "", bool enable = false)
{
}
public static void MustNotUseNamedArgsInCall(bool enable, string start = "")
{
}
}
public void Use(int a, int b, int c)
{
}

2
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -1310,7 +1310,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1310,7 +1310,7 @@ namespace ICSharpCode.Decompiler.CSharp
decompileRun.RecordDecompilers.Add(typeDef, recordDecompiler);
// With C# 9 records, the relative order of fields and properties matters:
IEnumerable<IMember> fieldsAndProperties = typeDef.IsRecord
IEnumerable<IMember> fieldsAndProperties = isRecord
? recordDecompiler.FieldsAndProperties
: typeDef.Fields.Concat<IMember>(typeDef.Properties);

74
ICSharpCode.Decompiler/CSharp/CallBuilder.cs

@ -64,6 +64,30 @@ namespace ICSharpCode.Decompiler.CSharp @@ -64,6 +64,30 @@ namespace ICSharpCode.Decompiler.CSharp
return FirstOptionalArgumentIndex;
}
public string[] GetArgumentNames(int skipCount = 0)
{
string[] argumentNames = ArgumentNames;
if (AddNamesToPrimitiveValues && IsPrimitiveValue.Any() && !IsExpandedForm
&& !ParameterNames.Any(string.IsNullOrEmpty))
{
Debug.Assert(skipCount == 0);
if (argumentNames == null)
{
argumentNames = new string[Arguments.Length];
}
for (int i = 0; i < Arguments.Length; i++)
{
if (IsPrimitiveValue[i] && argumentNames[i] == null)
{
argumentNames[i] = ParameterNames[i];
}
}
}
return argumentNames;
}
public IList<ResolveResult> GetArgumentResolveResults(int skipCount = 0)
{
var expectedParameters = ExpectedParameters;
@ -95,33 +119,17 @@ namespace ICSharpCode.Decompiler.CSharp @@ -95,33 +119,17 @@ namespace ICSharpCode.Decompiler.CSharp
public IEnumerable<Expression> GetArgumentExpressions(int skipCount = 0)
{
if (AddNamesToPrimitiveValues && IsPrimitiveValue.Any() && !IsExpandedForm
&& !ParameterNames.Any(p => string.IsNullOrEmpty(p)))
{
Debug.Assert(skipCount == 0);
if (ArgumentNames == null)
{
ArgumentNames = new string[Arguments.Length];
}
for (int i = 0; i < Arguments.Length; i++)
{
if (IsPrimitiveValue[i] && ArgumentNames[i] == null)
{
ArgumentNames[i] = ParameterNames[i];
}
}
}
var argumentNames = GetArgumentNames(skipCount);
int argumentCount = GetActualArgumentCount();
var useImplicitlyTypedOut = UseImplicitlyTypedOut;
if (ArgumentNames == null)
if (argumentNames == null)
{
return Arguments.Skip(skipCount).Take(argumentCount).Select(arg => AddAnnotations(arg.Expression));
}
else
{
Debug.Assert(skipCount == 0);
return Arguments.Take(argumentCount).Zip(ArgumentNames.Take(argumentCount),
return Arguments.Take(argumentCount).Zip(argumentNames.Take(argumentCount),
(arg, name) => {
if (name == null)
return AddAnnotations(arg.Expression);
@ -531,6 +539,10 @@ namespace ICSharpCode.Decompiler.CSharp @@ -531,6 +539,10 @@ namespace ICSharpCode.Decompiler.CSharp
Expression targetExpr;
string methodName = method.Name;
AstNodeCollection<AstType> typeArgumentList;
if ((transform & CallTransformation.NoNamedArgsForPrettiness) != 0)
{
argumentList.AddNamesToPrimitiveValues = false;
}
if ((transform & CallTransformation.NoOptionalArgumentAllowed) != 0)
{
argumentList.FirstOptionalArgumentIndex = -1;
@ -674,7 +686,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -674,7 +686,7 @@ namespace ICSharpCode.Decompiler.CSharp
argumentList.UseImplicitlyTypedOut = false;
var transform = GetRequiredTransformationsForCall(expectedTargetDetails, method, ref unused,
ref argumentList, CallTransformation.None, out _);
Debug.Assert(transform == CallTransformation.None || transform == CallTransformation.NoOptionalArgumentAllowed);
Debug.Assert((transform & ~(CallTransformation.NoOptionalArgumentAllowed | CallTransformation.NoNamedArgsForPrettiness)) == 0);
// Calls with only one argument do not need an array initializer expression to wrap them.
// Any special cases are handled by the caller (i.e., ExpressionBuilder.TranslateObjectAndCollectionInitializer)
@ -1121,7 +1133,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1121,7 +1133,8 @@ namespace ICSharpCode.Decompiler.CSharp
/// Add calls to AsRefReadOnly for in parameters that did not have an explicit DirectionExpression yet.
/// </summary>
EnforceExplicitIn = 8,
All = 0xf,
NoNamedArgsForPrettiness = 0x10,
All = 0x1f,
}
private CallTransformation GetRequiredTransformationsForCall(ExpectedTargetDetails expectedTargetDetails, IMethod method,
@ -1198,7 +1211,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1198,7 +1211,7 @@ namespace ICSharpCode.Decompiler.CSharp
bool skipTargetCast = method.Accessibility <= Accessibility.Protected && expressionBuilder.IsBaseTypeOfCurrentType(method.DeclaringTypeDefinition);
OverloadResolutionErrors errors;
while ((errors = IsUnambiguousCall(expectedTargetDetails, method, targetResolveResult, typeArguments,
argumentList.GetArgumentResolveResults().ToArray(), argumentList.ArgumentNames, argumentList.FirstOptionalArgumentIndex, out foundMethod,
argumentList.GetArgumentResolveResults().ToArray(), argumentList.GetArgumentNames(), argumentList.FirstOptionalArgumentIndex, out foundMethod,
out var bestCandidateIsExpandedForm)) != OverloadResolutionErrors.None || bestCandidateIsExpandedForm != argumentList.IsExpandedForm)
{
switch (errors)
@ -1228,7 +1241,11 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1228,7 +1241,11 @@ namespace ICSharpCode.Decompiler.CSharp
default:
// TODO : implement some more intelligent algorithm that decides which of these fixes (cast args, add target, cast target, add type args)
// is best in this case. Additionally we should not cast all arguments at once, but step-by-step try to add only a minimal number of casts.
if (argumentList.FirstOptionalArgumentIndex >= 0)
if (argumentList.AddNamesToPrimitiveValues)
{
argumentList.AddNamesToPrimitiveValues = false;
}
else if (argumentList.FirstOptionalArgumentIndex >= 0)
{
argumentList.FirstOptionalArgumentIndex = -1;
}
@ -1293,6 +1310,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1293,6 +1310,8 @@ namespace ICSharpCode.Decompiler.CSharp
transform |= CallTransformation.RequireTypeArguments;
if (argumentList.FirstOptionalArgumentIndex < 0)
transform |= CallTransformation.NoOptionalArgumentAllowed;
if (!argumentList.AddNamesToPrimitiveValues)
transform |= CallTransformation.NoNamedArgsForPrettiness;
return transform;
}
@ -1438,7 +1457,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1438,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);
@ -1766,9 +1785,14 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1766,9 +1785,14 @@ namespace ICSharpCode.Decompiler.CSharp
{
while (IsUnambiguousCall(expectedTargetDetails, method, null, Empty<IType>.Array,
argumentList.GetArgumentResolveResults().ToArray(),
argumentList.ArgumentNames, argumentList.FirstOptionalArgumentIndex, out _,
argumentList.GetArgumentNames(), argumentList.FirstOptionalArgumentIndex, out _,
out var bestCandidateIsExpandedForm) != OverloadResolutionErrors.None || bestCandidateIsExpandedForm != argumentList.IsExpandedForm)
{
if (argumentList.AddNamesToPrimitiveValues)
{
argumentList.AddNamesToPrimitiveValues = false;
continue;
}
if (argumentList.FirstOptionalArgumentIndex >= 0)
{
argumentList.FirstOptionalArgumentIndex = -1;

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