Skip to content
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
36 changes: 36 additions & 0 deletions src/linker/Linker/TypeMapInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down Expand Up @@ -407,6 +410,39 @@ static bool TypeMatch (GenericParameter a, GenericParameter b)
return true;
}

static bool TypeMatch (FunctionPointerType a, FunctionPointerType b)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not 100% this is covering everything, but it at least appears to work correctly with the function pointers I have declared.

Copy link
Member Author

@tannergooding tannergooding Nov 4, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most notably, function pointers can't be generic but they can have generic use generic types for the return type or parameter if the parent scope is generic

There isn't a context available here, so I'm unsure if there needs to be any additional handling or logic to cover that scenario.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are also modreq that can appear as part of the calling convention. I think modreq are handled further up in the TypeMatch logic, however.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the generic return/parameter types are covered by the GenericInstanceType case in TypeMatch (TypeSpecification, TypeSpecification).

{
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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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<void> fnptr)
{
}

[Kept]
public void Method (delegate* unmanaged<void> fnptr)
{
}
}

interface I
{
void Unused (delegate* unmanaged<void> fnptr);

void Method (delegate* unmanaged<void> fnptr);
}
}
}
Original file line number Diff line number Diff line change
@@ -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<void> fnptr)
{
}

[Kept]
public void Method (delegate* unmanaged<void> fnptr)
{
}
}
}
}
5 changes: 5 additions & 0 deletions test/Mono.Linker.Tests/TestCases/TestDatabase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ public static IEnumerable<TestCaseData> FeatureSettingsTests ()
return NUnitCasesBySuiteName ("FeatureSettings");
}

public static IEnumerable<TestCaseData> FunctionPointersTests ()
{
return NUnitCasesBySuiteName ("FunctionPointers");
}

public static IEnumerable<TestCaseData> GenericsTests ()
{
return NUnitCasesBySuiteName ("Generics");
Expand Down
6 changes: 6 additions & 0 deletions test/Mono.Linker.Tests/TestCases/TestSuites.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down