Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C# 9 Covariant Returns #2470

Merged
merged 1 commit into from
Aug 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@
<Compile Include="TestCases\Correctness\DeconstructionTests.cs" />
<Compile Include="TestCases\Correctness\DynamicTests.cs" />
<Compile Include="TestCases\Correctness\StringConcat.cs" />
<None Include="TestCases\Pretty\CovariantReturns.cs" />
<Compile Include="TestCases\VBPretty\VBPropertiesTest.cs" />
<None Include="TestCases\ILPretty\Issue2260SwitchString.cs" />
<None Include="TestCases\Pretty\Records.cs" />
Expand Down
6 changes: 6 additions & 0 deletions ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,12 @@ public void CS9_ExtensionGetEnumerator([ValueSource(nameof(dotnetCoreOnlyOptions
RunForLibrary(cscOptions: cscOptions | CompilerOptions.Preview);
}

[Test]
public void CovariantReturns([ValueSource(nameof(dotnetCoreOnlyOptions))] CompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions | CompilerOptions.Preview);
}

void RunForLibrary([CallerMemberName] string testName = null, AssemblerOptions asmOptions = AssemblerOptions.None, CompilerOptions cscOptions = CompilerOptions.None, DecompilerSettings decompilerSettings = null)
{
Run(testName, asmOptions | AssemblerOptions.Library, cscOptions | CompilerOptions.Library, decompilerSettings);
Expand Down
50 changes: 50 additions & 0 deletions ICSharpCode.Decompiler.Tests/TestCases/Pretty/CovariantReturns.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
namespace ICSharpCode.Decompiler.Tests.TestCases.CovariantReturns
{
public abstract class Base
{
public abstract Base Instance { get; }

public abstract Base this[int index] { get; }

public virtual Base Build()
{
throw null;
}

protected abstract Base SetParent(object parent);
}

public class Derived : Base
{
public override Derived Instance { get; }

public override Derived this[int index] {
get {
throw null;
}
}

public override Derived Build()
{
throw null;
}

protected override Derived SetParent(object parent)
{
throw null;
}
}

public class UseSites
{
public Base Test(Base x)
{
return x.Build();
}

public Derived Test(Derived x)
{
return x.Build();
}
}
}
23 changes: 23 additions & 0 deletions ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1451,9 +1451,24 @@ EntityDeclaration DoDecompile(IMethod method, DecompileRun decompileRun, ITypeRe
{
SetNewModifier(methodDecl);
}
if (IsCovariantReturnOverride(method))
{
RemoveAttribute(methodDecl, KnownAttribute.PreserveBaseOverrides);
methodDecl.Modifiers &= ~(Modifiers.New | Modifiers.Virtual);
methodDecl.Modifiers |= Modifiers.Override;
}
return methodDecl;
}

private bool IsCovariantReturnOverride(IEntity entity)
{
if (!settings.CovariantReturns)
return false;
if (!entity.HasAttribute(KnownAttribute.PreserveBaseOverrides))
return false;
return true;
}

internal static bool IsWindowsFormsInitializeComponentMethod(IMethod method)
{
return method.ReturnType.Kind == TypeKind.Void && method.Name == "InitializeComponent" && method.DeclaringTypeDefinition.GetNonInterfaceBaseTypes().Any(t => t.FullName == "System.Windows.Forms.Control");
Expand Down Expand Up @@ -1766,7 +1781,15 @@ EntityDeclaration DoDecompile(IProperty property, DecompileRun decompileRun, ITy
var accessorHandle = (MethodDefinitionHandle)(property.Getter ?? property.Setter).MetadataToken;
var accessor = metadata.GetMethodDefinition(accessorHandle);
if (!accessorHandle.GetMethodImplementations(metadata).Any() && accessor.HasFlag(System.Reflection.MethodAttributes.Virtual) == accessor.HasFlag(System.Reflection.MethodAttributes.NewSlot))
{
SetNewModifier(propertyDecl);
}
if (IsCovariantReturnOverride(property.Getter))
{
RemoveAttribute(getter, KnownAttribute.PreserveBaseOverrides);
propertyDecl.Modifiers &= ~(Modifiers.New | Modifiers.Virtual);
propertyDecl.Modifiers |= Modifiers.Override;
}
return propertyDecl;
}
catch (Exception innerException) when (!(innerException is OperationCanceledException || innerException is DecompilerException))
Expand Down
22 changes: 21 additions & 1 deletion ICSharpCode.Decompiler/DecompilerSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,12 +139,14 @@ public void SetLanguageVersion(CSharp.LanguageVersion languageVersion)
recordClasses = false;
withExpressions = false;
usePrimaryConstructorSyntax = false;
covariantReturns = false;
}
}

public CSharp.LanguageVersion GetMinimumRequiredVersion()
{
if (nativeIntegers || initAccessors || functionPointers || forEachWithGetEnumeratorExtension || recordClasses)
if (nativeIntegers || initAccessors || functionPointers || forEachWithGetEnumeratorExtension
|| recordClasses || withExpressions || usePrimaryConstructorSyntax || covariantReturns)
return CSharp.LanguageVersion.Preview;
if (nullableReferenceTypes || readOnlyMethods || asyncEnumerator || asyncUsingAndForEachStatement
|| staticLocalFunctions || ranges || switchExpressions)
Expand Down Expand Up @@ -193,6 +195,24 @@ public bool NativeIntegers {
}
}

bool covariantReturns = true;

/// <summary>
/// Decompile C# 9 covariant return types.
/// </summary>
[Category("C# 9.0 / VS 2019.8")]
[Description("DecompilerSettings.CovariantReturns")]
public bool CovariantReturns {
get { return covariantReturns; }
set {
if (covariantReturns != value)
{
covariantReturns = value;
OnPropertyChanged();
}
}
}

bool initAccessors = true;

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,12 @@ public enum KnownAttribute

// C# 9 attributes:
NativeInteger,
PreserveBaseOverrides,
}

static class KnownAttributes
{
internal const int Count = (int)KnownAttribute.NativeInteger + 1;
internal const int Count = (int)KnownAttribute.PreserveBaseOverrides + 1;

static readonly TopLevelTypeName[] typeNames = new TopLevelTypeName[Count]{
default,
Expand Down Expand Up @@ -167,6 +168,7 @@ static class KnownAttributes
new TopLevelTypeName("System.Security.Permissions", "PermissionSetAttribute"),
// C# 9 attributes:
new TopLevelTypeName("System.Runtime.CompilerServices", "NativeIntegerAttribute"),
new TopLevelTypeName("System.Runtime.CompilerServices", "PreserveBaseOverridesAttribute"),
};

public static ref readonly TopLevelTypeName GetTypeName(this KnownAttribute attr)
Expand Down