diff --git a/src/linker/Linker/TypeMapInfo.cs b/src/linker/Linker/TypeMapInfo.cs index 2d1350290522..f14036942b91 100644 --- a/src/linker/Linker/TypeMapInfo.cs +++ b/src/linker/Linker/TypeMapInfo.cs @@ -369,6 +369,9 @@ static bool TypeMatch (TypeSpecification a, TypeSpecification b) if (a is IModifierType mta) return TypeMatch (mta, (IModifierType) b); + if (a is FunctionPointerType fpta) + return TypeMatch (fpta, (FunctionPointerType) b); + return TypeMatch (a.ElementType, b.ElementType); } @@ -407,6 +410,39 @@ static bool TypeMatch (GenericParameter a, GenericParameter b) return true; } + static bool TypeMatch (FunctionPointerType a, FunctionPointerType b) + { + if (a.HasParameters != b.HasParameters) + return false; + + if (a.CallingConvention != b.CallingConvention) + return false; + + // we need to track what the generic parameter represent - as we cannot allow it to + // differ between the return type or any parameter + if (a.ReturnType is not TypeReference aReturnType || + b.ReturnType is not TypeReference bReturnType || + !TypeMatch (aReturnType, bReturnType)) + return false; + + if (!a.HasParameters) + return true; + + var ap = a.Parameters; + var bp = b.Parameters; + if (ap.Count != bp.Count) + return false; + + for (int i = 0; i < ap.Count; i++) { + if (a.Parameters[i].ParameterType is not TypeReference aParameterType || + b.Parameters[i].ParameterType is not TypeReference bParameterType || + !TypeMatch (aParameterType, bParameterType)) + return false; + } + + return true; + } + static bool TypeMatch (TypeReference a, TypeReference b) { if (a is TypeSpecification || b is TypeSpecification) { diff --git a/test/Mono.Linker.Tests.Cases/FunctionPointers/CanCompileInterfaceWithFunctionPointerParameter.cs b/test/Mono.Linker.Tests.Cases/FunctionPointers/CanCompileInterfaceWithFunctionPointerParameter.cs new file mode 100644 index 000000000000..50c4da0fd1f1 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/FunctionPointers/CanCompileInterfaceWithFunctionPointerParameter.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.FunctionPointers +{ + [SetupCompileArgument ("/unsafe")] + unsafe class CanCompileInterfaceWithFunctionPointerParameter + { + public static void Main () + { + new CanCompileInterfaceWithFunctionPointerParameter.B ().Method (null); + } + + [KeptMember (".ctor()")] + class B : I + { + public void Unused (delegate* unmanaged fnptr) + { + } + + [Kept] + public void Method (delegate* unmanaged fnptr) + { + } + } + + interface I + { + void Unused (delegate* unmanaged fnptr); + + void Method (delegate* unmanaged fnptr); + } + } +} diff --git a/test/Mono.Linker.Tests.Cases/FunctionPointers/CanCompileMethodWithFunctionPointerParameter.cs b/test/Mono.Linker.Tests.Cases/FunctionPointers/CanCompileMethodWithFunctionPointerParameter.cs new file mode 100644 index 000000000000..fad07b5eab27 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/FunctionPointers/CanCompileMethodWithFunctionPointerParameter.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.FunctionPointers +{ + [SetupCompileArgument ("/unsafe")] + unsafe class CanCompileMethodWithFunctionPointerParameter + { + public static void Main () + { + new CanCompileMethodWithFunctionPointerParameter.B ().Method (null); + } + + [KeptMember (".ctor()")] + class B + { + public void Unused (delegate* unmanaged fnptr) + { + } + + [Kept] + public void Method (delegate* unmanaged fnptr) + { + } + } + } +} diff --git a/test/Mono.Linker.Tests/TestCases/TestDatabase.cs b/test/Mono.Linker.Tests/TestCases/TestDatabase.cs index 335a338e1aec..5dd8ad354c63 100644 --- a/test/Mono.Linker.Tests/TestCases/TestDatabase.cs +++ b/test/Mono.Linker.Tests/TestCases/TestDatabase.cs @@ -86,6 +86,11 @@ public static IEnumerable FeatureSettingsTests () return NUnitCasesBySuiteName ("FeatureSettings"); } + public static IEnumerable FunctionPointersTests () + { + return NUnitCasesBySuiteName ("FunctionPointers"); + } + public static IEnumerable GenericsTests () { return NUnitCasesBySuiteName ("Generics"); diff --git a/test/Mono.Linker.Tests/TestCases/TestSuites.cs b/test/Mono.Linker.Tests/TestCases/TestSuites.cs index 3a07cbc77a2c..51003ce2d4e0 100644 --- a/test/Mono.Linker.Tests/TestCases/TestSuites.cs +++ b/test/Mono.Linker.Tests/TestCases/TestSuites.cs @@ -103,6 +103,12 @@ public void FeatureSettingsTests (TestCase testCase) Run (testCase); } + [TestCaseSource (typeof (TestDatabase), nameof (TestDatabase.FunctionPointersTests))] + public void FunctionPointerTests (TestCase testCase) + { + Run (testCase); + } + [TestCaseSource (typeof (TestDatabase), nameof (TestDatabase.GenericsTests))] public void GenericsTests (TestCase testCase) {