Skip to content

Conversation

@jgh07
Copy link
Contributor

@jgh07 jgh07 commented Jan 30, 2026

This implements #75348. It is not yet ready to be merged, but I'd appreciate a preliminary review before I tackle the remaining issues.

  • Type.MakeFunctionPointerSignatureType and Type.MakeModifiedSignatureType are implemented.
  • Type.MakeFunctionPointerType is not implemented yet. I am still debating wheter to include it in this PR.
  • EmitCalli is currently only implemented on ILGeneratorImpl. DynamicILGenerator is a wholly different code base, therefore I cannot make use of the helpers I added in Support function pointer types in System.Reflection.Emit #119935. I might have to reimplement parts of the logic I did for that PR.

Copilot AI review requested due to automatic review settings January 30, 2026 22:34
@dotnet-policy-service dotnet-policy-service bot added the community-contribution Indicates that the PR has been added by a community member label Jan 30, 2026
@dotnet-policy-service
Copy link
Contributor

Tagging subscribers to this area: @dotnet/area-system-reflection
See info in area-owners.md if you want to be subscribed.

public abstract void EmitCalli(OpCode opcode, CallingConvention unmanagedCallConv, Type? returnType, Type[]? parameterTypes);

/// <summary>
/// Puts a <see cref="OpCodes.Calli"/> instruction onto the Microsoft intermediate language (MSIL) stream,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I kept this description largely the same as the existing overloads. I think it might make more sense to update it though, since as far as I'm concerned the term MSIL was dropped in favor of CIL.

public sealed override int GetHashCode() => base.GetHashCode();
#endif
public sealed override Type UnderlyingSystemType => this; // Equals(Type) depends on this.
public override Type UnderlyingSystemType => this; // Equals(Type) depends on this.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This feels very wrong, and I hope there is a better way.
The comment is there for a reason, and it does indeed break Equals(Type) for SignatureModifiedType, but I honestly couldn't think of a better way to make the original, non-signature type available.

Copy link
Member

Choose a reason for hiding this comment

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

I cannot think of anything better.

WriteSignatureForType(retTypeEncoder.Type(), returnType, module);
Type returnTypeToWrite = returnType;
if (returnTypeToWrite.IsSignatureType)
returnTypeToWrite = returnTypeToWrite.UnderlyingSystemType;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not a no-op because of my override hack in SignatureModifiedType. I'd love to remove this in favor of a better solution.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR implements new function pointer APIs as proposed in issue #75348, enabling better support for function pointer types in IL generation and reflection scenarios.

Changes:

  • Adds Type.MakeFunctionPointerSignatureType and Type.MakeModifiedSignatureType static factory methods for creating signature types
  • Implements ILGenerator.EmitCalli(Type functionPointerType) overload in ILGeneratorImpl for modern function pointer calling conventions
  • Adds new internal SignatureFunctionPointerType and SignatureModifiedType classes to represent these signature types

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
System.Runtime.cs Adds public API surface for new function pointer and modified signature type factory methods
Type.cs Implements MakeFunctionPointerSignatureType and MakeModifiedSignatureType factory methods with XML documentation
SignatureFunctionPointerType.cs New internal class representing function pointer signature types with support for calling conventions
SignatureModifiedType.cs New internal class representing types with required/optional custom modifiers
SignatureType.cs Changes UnderlyingSystemType from sealed to virtual to allow SignatureModifiedType to override it
ILGenerator.cs Adds new EmitCalli overload accepting function pointer Type with XML documentation
ILGeneratorImpl.cs Implements the new EmitCalli overload with stack tracking and signature token generation
SignatureHelper.cs Makes WriteSignatureForFunctionPointerType internal and adds logic to unwrap SignatureTypes
ModuleBuilderImpl.cs Adds GetFunctionPointerSignatureToken method to generate standalone signatures for calli instructions
DynamicILGenerator.cs Adds stub TODO implementation for EmitCalli - not yet functional
System.Reflection.Emit.ILGeneration.cs Adds public API surface for new EmitCalli overload
Strings.resx Adds error message for invalid function pointer type arguments
System.Private.CoreLib.Shared.projitems Adds new SignatureFunctionPointerType and SignatureModifiedType to project, plus unrelated whitespace fix
SignatureTypes.cs Adds tests for the new MakeFunctionPointerSignatureType and MakeModifiedSignatureType methods
AssemblySaveILGeneratorTests.cs Adds end-to-end test for EmitCalli with function pointer types
Utilities.cs Adds ClassWithFunctionPointer test helper class
Comments suppressed due to low confidence (1)

src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/SignatureHelper.cs:6

  • This using directive appears to be unused. There are no references to System.IO types in this file. Consider removing it to keep the code clean.
using System.Reflection.Metadata;

Comment on lines +26 to +27
public override Type[] GetFunctionPointerCallingConventions() => _callingConventions;
public override Type[] GetFunctionPointerParameterTypes() => _parameterTypes;
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

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

These methods return the internal arrays directly without cloning, which allows callers to mutate the internal state. This violates encapsulation and could lead to unexpected behavior. These methods should return clones of the arrays, similar to how SignatureConstructedGenericType.GenericTypeArguments clones the array before returning it.

Suggested change
public override Type[] GetFunctionPointerCallingConventions() => _callingConventions;
public override Type[] GetFunctionPointerParameterTypes() => _parameterTypes;
public override Type[] GetFunctionPointerCallingConventions() => (Type[])_callingConventions.Clone();
public override Type[] GetFunctionPointerParameterTypes() => (Type[])_parameterTypes.Clone();

Copilot uses AI. Check for mistakes.
Comment on lines +20 to +21
public override Type[] GetRequiredCustomModifiers() => _requiredCustomModifiers;
public override Type[] GetOptionalCustomModifiers() => _optionalCustomModifiers;
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

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

This method returns the internal arrays directly without cloning, which allows callers to mutate the internal state. This violates encapsulation and could lead to unexpected behavior. These methods should return clones of the arrays, similar to how SignatureConstructedGenericType.GenericTypeArguments clones the array before returning it.

Suggested change
public override Type[] GetRequiredCustomModifiers() => _requiredCustomModifiers;
public override Type[] GetOptionalCustomModifiers() => _optionalCustomModifiers;
public override Type[] GetRequiredCustomModifiers() => (Type[])_requiredCustomModifiers.Clone();
public override Type[] GetOptionalCustomModifiers() => (Type[])_optionalCustomModifiers.Clone();

Copilot uses AI. Check for mistakes.
/// <returns>A <see cref="Type"/> object representing the constructed function pointer signature.</returns>
public static Type MakeFunctionPointerSignatureType(Type? returnType, Type[]? parameterTypes, bool isUnmanaged = false, Type[]? callingConventions = null)
{
returnType ??= typeof(void);
Copy link
Member

Choose a reason for hiding this comment

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

MakeFunctionPointerSignatureType is meant to work for System.Type from any reflection universe. I do not think we want to be substituting null with typeof(void). It would make it tied to runtime reflection universe.

Copy link
Member

Choose a reason for hiding this comment

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

We may want to make the returnType argument non-nullable to avoid this substitution.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree, I think that's sensible.

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

Labels

area-System.Reflection community-contribution Indicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants