Skip to content

Commit

Permalink
Merge pull request icsharpcode#2470 from icsharpcode/covariant-returns
Browse files Browse the repository at this point in the history
C# 9 Covariant Returns
  • Loading branch information
ElektroKill committed Nov 6, 2021
1 parent f593880 commit c86a9d8
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,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 @@ -569,6 +569,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 @@ -1215,9 +1215,24 @@ EntityDeclaration DoDecompile(Decompiler.TypeSystem.IMethod method, DecompileRun
if (method.SymbolKind == SymbolKind.Method && !method.IsExplicitInterfaceImplementation && methodDefinition.IsVirtual == methodDefinition.IsNewSlot) {
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(ICSharpCode.Decompiler.TypeSystem.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 @@ -1470,7 +1485,15 @@ EntityDeclaration DoDecompile(IProperty property, DecompileRun decompileRun, ITy
}
var accessor = (MethodDef)(property.Getter ?? property.Setter).MetadataToken;
if (!accessor.HasOverrides && accessor.IsVirtual == accessor.IsNewSlot)
{
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)) {
throw new DecompilerException(property.MetadataToken, innerException);
Expand Down
22 changes: 21 additions & 1 deletion ICSharpCode.Decompiler/DecompilerSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,12 +127,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)
return CSharp.LanguageVersion.CSharp8_0;
Expand Down Expand Up @@ -175,6 +177,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

0 comments on commit c86a9d8

Please sign in to comment.