Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[mono] Function pointer type equality and type compatibility are not properly handled #90308

Open
2 tasks
ivanpovazan opened this issue Aug 10, 2023 · 2 comments
Open
2 tasks
Assignees
Milestone

Comments

@ivanpovazan
Copy link
Member

Description

It was noticed that Mono does not handle function pointer type equality and type compatibility the same way CoreCLR does.
There seem to be two related underlying issues:

  • 1. Mono should ignore custom modifiers on function pointer parameters and calling convention when comparing function pointer types
  • 2. Mono should properly implement isinst in case of testing function pointer type compatibility against array of function pointers (respecting 1. from above)

Noticed in #89712 while enabling function pointer introspection API with Mono reflection in runtime tests:

// These are runtime-specific tests not shared with MetadataLoadContext.
// Using arrays in the manner below allows for use of the "is" keyword.
// The use of 'object' will call into the runtime to compare but using a strongly-typed
// function pointer without 'object' causes C# to hard-code the result.
public partial class FunctionPointerTests

Repro

Here is a sample program to illustrate the behaviours:

using System;
using System.Reflection;
using System.Runtime.CompilerServices;

namespace HelloWorld
{
    internal unsafe class Program
    {
        private const BindingFlags Bindings = BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly;
        
        public static delegate* unmanaged[Cdecl]<int> fptr1;
        public static delegate* unmanaged[Stdcall]<int> fptr2;
        private static void Main(string[] args)
        {
            FieldInfo fi1 = typeof(Program).GetField(nameof(Program.fptr1), Bindings);
            Type ft1 = fi1.FieldType;

            FieldInfo fi2 = typeof(Program).GetField(nameof(Program.fptr2), Bindings);
            Type ft2 = fi2.FieldType;

            if (ft1 == ft2)
                Console.WriteLine("Function pointer types are equal");
            else
                Console.WriteLine("Function pointer types are not equal");

            object arrayOfFptrs1 = new delegate*<int>[1];
            if (arrayOfFptrs1 is delegate*<int>[])
                Console.WriteLine("Is type compatible");
            else
                Console.WriteLine("Is not type compatible");

            object arrayOfFptrs2 = new delegate*<ref int, void>[1];
            if (arrayOfFptrs2 is delegate*<out int, void>[])
                Console.WriteLine("Is type compatible");
            else
                Console.WriteLine("Is not type compatible");
        }
    }
}

Expected behaviour

Program should output:

Function pointer types are equal
Is type compatible
Is type compatible

Actual behaviour

Program outputs:

Function pointer types are not equal
Is not type compatible
Is not type compatible
@ivanpovazan
Copy link
Member Author

/cc: @lambdageek

@lambdageek
Copy link
Member

This is partly because mono detects the "unmanaged" calling convention on function pointer signatures during loading and then eagerly looks at the custom modifiers and adjusts the calling convention right away on the MonoMethodSignature. It seems that instead we should leave it as "unmanaged" and only dip into the cmods in the marshaling logic that actually needs to pick a calling convention.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants