From d9874380cde97604a7975f1afa75c79c41fbef3b Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Tue, 29 Dec 2020 18:26:30 +0100 Subject: [PATCH] Records: support generic records --- .../TestCases/Pretty/Records.cs | 18 ++++++++++++++++++ .../CSharp/RecordDecompiler.cs | 19 ++++++++++++++----- .../CSharp/Syntax/TypeSystemAstBuilder.cs | 2 +- .../TypeSystem/TypeSystemExtensions.cs | 17 +++++++++++++++++ 4 files changed, 50 insertions(+), 6 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Records.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Records.cs index 07865dc19..bba7d911b 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Records.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Records.cs @@ -13,6 +13,18 @@ public string S = "abc"; } + public record Pair + { + public A First { + get; + init; + } + public B Second { + get; + init; + } + } + public record Properties { public int A { @@ -42,3 +54,9 @@ } } } +namespace System.Runtime.CompilerServices +{ + internal class IsExternalInit + { + } +} diff --git a/ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs b/ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs index da7d74b49..25f80ed02 100644 --- a/ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs @@ -53,9 +53,11 @@ namespace ICSharpCode.Decompiler.CSharp void DetectAutomaticProperties() { - foreach (var p in recordTypeDef.Properties) + var subst = recordTypeDef.AsParameterizedType().GetSubstitution(); + foreach (var property in recordTypeDef.Properties) { cancellationToken.ThrowIfCancellationRequested(); + var p = (IProperty)property.Specialize(subst); if (IsAutoProperty(p, out var field)) { backingFieldToAutoProperty.Add(field, p); @@ -152,8 +154,9 @@ namespace ICSharpCode.Decompiler.CSharp // need to detect the correct interleaving. // We could try to detect this from the PrintMembers body, but let's initially // restrict ourselves to the common case where the record only uses properties. - return recordTypeDef.Properties.Concat( - recordTypeDef.Fields.Where(f => !backingFieldToAutoProperty.ContainsKey(f)) + var subst = recordTypeDef.AsParameterizedType().GetSubstitution(); + return recordTypeDef.Properties.Select(p => p.Specialize(subst)).Concat( + recordTypeDef.Fields.Select(f => (IField)f.Specialize(subst)).Where(f => !backingFieldToAutoProperty.ContainsKey(f)) ).ToList(); } @@ -417,9 +420,15 @@ namespace ICSharpCode.Decompiler.CSharp val = addressOf.Value; } } + else if (val is Box box) + { + if (!NormalizeTypeVisitor.TypeErasure.EquivalentTypes(box.Type, member.ReturnType)) + return false; + val = box.Argument; + } if (val is CallInstruction getterCall && member is IProperty property) { - if (!getterCall.Method.MemberDefinition.Equals(property.Getter.MemberDefinition)) + if (!getterCall.Method.Equals(property.Getter)) return false; if (getterCall.Arguments.Count != 1) return false; @@ -430,7 +439,7 @@ namespace ICSharpCode.Decompiler.CSharp { if (!target.MatchLdThis()) return false; - if (!field.MemberDefinition.Equals(member)) + if (!field.Equals(member)) return false; } else diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs index 08f87fa5a..1e04e5402 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs @@ -1804,7 +1804,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax else if (SupportRecordClasses && typeDefinition.IsRecord && baseType.Name == "IEquatable" && baseType.Namespace == "System" && baseType.TypeArguments.Count == 1 - && baseType.TypeArguments[0].Equals(typeDefinition)) + && baseType.TypeArguments[0].Equals(typeDefinition.AsParameterizedType())) { // omit "IEquatable" in records continue; diff --git a/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs b/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs index bf25900b6..993804d95 100644 --- a/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs +++ b/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs @@ -616,5 +616,22 @@ namespace ICSharpCode.Decompiler.TypeSystem } return null; } + + /// + /// When given a generic type definition, returns the self-parameterized type + /// (i.e. the type of "this" within the type definition). + /// When given a non-generic type definition, returns that definition unchanged. + /// + public static IType AsParameterizedType(this ITypeDefinition td) + { + if (td.TypeParameterCount == 0) + { + return td; + } + else + { + return new ParameterizedType(td, td.TypeArguments); + } + } } }