From fa3de7bfa4b7b97b394e5ad304ea283b772fb996 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 30 Aug 2026 12:09:40 +0200 Subject: [PATCH] MergeSimilarTypes: add support for more kinds of types --- .../Semantics/TypeInferenceTests.cs | 25 ++++++++++ .../CSharp/Resolver/TypeInference.cs | 50 +++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/ICSharpCode.Decompiler.Tests/Semantics/TypeInferenceTests.cs b/ICSharpCode.Decompiler.Tests/Semantics/TypeInferenceTests.cs index 8ff5c4dde..605d76e27 100644 --- a/ICSharpCode.Decompiler.Tests/Semantics/TypeInferenceTests.cs +++ b/ICSharpCode.Decompiler.Tests/Semantics/TypeInferenceTests.cs @@ -23,6 +23,7 @@ using System.Collections.Immutable; using System.Collections.ObjectModel; using System.IO; using System.Linq; +using System.Reflection.Metadata; using ICSharpCode.Decompiler.CSharp.Resolver; using ICSharpCode.Decompiler.Metadata; @@ -713,6 +714,15 @@ namespace ICSharpCode.Decompiler.Tests.Semantics ImmutableArray.CreateRange(elementNames)); } + FunctionPointerType MakeFunctionPointerType(ICompilation comp, IType returnType) + { + return new FunctionPointerType( + (MetadataModule)comp.MainModule, + SignatureCallingConvention.Default, ImmutableArray.Empty, + returnType, returnIsRefReadOnly: false, + ImmutableArray.Empty, ImmutableArray.Empty); + } + [Test] public void BestCommonTypeMergesTupleElementNames() { @@ -730,6 +740,21 @@ namespace ICSharpCode.Decompiler.Tests.Semantics Assert.That(success); } + [Test] + public void BestCommonTypeMergesFunctionPointerTupleElementNames() + { + var comp = RefAssemblyCompilation.Instance; + var inference = new TypeInference(comp); + + Assert.That( + inference.GetBestCommonType(new[] { + new ResolveResult(MakeFunctionPointerType(comp, MakeTupleType(comp, "a", "b"))), + new ResolveResult(MakeFunctionPointerType(comp, MakeTupleType(comp, "a", "c"))) + }, out bool success), + Is.EqualTo(MakeFunctionPointerType(comp, MakeTupleType(comp, "a", null)))); + Assert.That(success); + } + [Test] public void FixingMergesTupleElementNamesOfExactAndLowerBounds() { diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs b/ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs index 53816806c..f81bc7b50 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs @@ -1101,6 +1101,15 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver var nullability = MergeNullability(arrA.Nullability, arrB.Nullability, variance); return new ArrayType(arrA.Compilation, mergedElem, arrA.Dimensions, nullability); } + if (a is ByReferenceType refA && b is ByReferenceType refB) + { + var mergedElem = MergeSimilarTypes( + refA.ElementType, refB.ElementType, + variance.Combine(VarianceModifier.Invariant)); + if (mergedElem == null) + return null; + return new ByReferenceType(mergedElem); + } if (a is PointerType ptrA && b is PointerType ptrB) { var mergedElem = MergeSimilarTypes( @@ -1110,6 +1119,47 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver return null; return new PointerType(mergedElem); } + if (a is FunctionPointerType fnPtrA && b is FunctionPointerType fnPtrB + && fnPtrA.CallingConvention == fnPtrB.CallingConvention + && fnPtrA.CustomCallingConventions.SequenceEqual(fnPtrB.CustomCallingConventions) + && fnPtrA.ReturnIsRefReadOnly == fnPtrB.ReturnIsRefReadOnly + && fnPtrA.ParameterTypes.Length == fnPtrB.ParameterTypes.Length + && fnPtrA.ParameterReferenceKinds.SequenceEqual(fnPtrB.ParameterReferenceKinds)) + { + var mergedReturn = MergeSimilarTypes( + fnPtrA.ReturnType, fnPtrB.ReturnType, + variance.Combine(VarianceModifier.Covariant)); + if (mergedReturn == null) + return null; + var mergedParameters = ImmutableArray.CreateBuilder(fnPtrA.ParameterTypes.Length); + for (int i = 0; i < fnPtrA.ParameterTypes.Length; i++) + { + var mergedParameter = MergeSimilarTypes( + fnPtrA.ParameterTypes[i], fnPtrB.ParameterTypes[i], + variance.Combine(VarianceModifier.Contravariant)); + if (mergedParameter == null) + return null; + mergedParameters.Add(mergedParameter); + } + return fnPtrA.WithSignature(mergedReturn, mergedParameters.MoveToImmutable()); + } + if (a is ModifiedType modA && b is ModifiedType modB + && modA.Kind == modB.Kind + && modA.Modifier.Equals(modB.Modifier)) + { + var mergedElem = MergeSimilarTypes(modA.ElementType, modB.ElementType, variance); + if (mergedElem == null) + return null; + return new ModifiedType(modA.Modifier, mergedElem, modA.Kind == TypeKind.ModReq); + } + if (a is UnknownType unknownTypeA && b is UnknownType unknownTypeB + && unknownTypeA.FullTypeName == unknownTypeB.FullTypeName) + { + if (unknownTypeA.IsReferenceType == unknownTypeB.IsReferenceType) + return unknownTypeA; + else + return unknownTypeA.WithoutReferenceTypeKnowledge(); + } return null; }