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

[SwiftBindings] Handling metadata of primitive numeric Swift types with C# counterparts #2861

Open
jkurdek opened this issue Dec 6, 2024 · 4 comments
Assignees
Labels
area-SwiftBindings Swift bindings for .NET

Comments

@jkurdek
Copy link
Member

jkurdek commented Dec 6, 2024

Problem statement

One of the sets of types we are going to project into Swift are primitive numeric types which have their C# counterparts. Those should be blittable and have the same underlying representation on both C# and Swift side.

The interesting part happens when we are going to use those types in generic context.

Usage from C#

Unconstrained generics

When calling projected generic types / functions with primitive numerics from C# side we should not encounter any problems.

Constrained generics

Swift primitives conform to multiple protocols. We would like user to be able to call any projected generic method with a protocol constrain using a primitive conforming to the protocol.

Consider the following projected function. (ISendable is a projected protocol Sendable, it is one of the protocols which Swift.Int conforms to)

public static void AcceptSendable<T> (T value) where T : ISendable
{
}

in order to call the above projected projection using int on C# side we would need to make int somehow conform the the projected ISendable.

ABI

Unconstrained generics

In order to call a generic function with a numeric we will need to obtain its metadata pointer

Constrained generic

In addition to getting the metadata pointer we will have to obtain the Protocol Witness Table for the (type, protocol pair).

Solution Proposal

Usage from C#

The only scenario which requires a custom solution is calling generic types / functions with generic constraints. One of the ways to solve this problem is to require the user to explicitly box the primitive value in a type which conforms to the projected interfaces. Detailed description with an examples can be found here: https://github.com/dotnet/runtimelab/blob/feature/swift-bindings/src/docs/binding-generics.md#supporting-generic-constraints-on-types-present-in-both-c-and-swift

ABI

Metadata

I can see several ways of obtaining the metadata for the primitive types.

Lookup dictionary

We could maintain a dictionary of primitive types and their corresponding metadata accessors. When getting metadata of a type we could first check if it is a ISwiftObject and then if the check fails query this dictionary to try to obtain the metadata accessor.

Using boxes

Assuming the decided to solve the constraints problem using boxing. The boxes would implement the ISwiftObject interface, meaning that they would implement all the necessary metadata accessors. During marshalling we could box all the numeric values / conditionally replace T with box's type when calling static metadata accessors.

PWT

Very similar to the Metadata, depends on the solution of the C# side usage problem stated above. I could imagine that the box used to satisfy the protocol constraint on the c# side would implement all the necessary PWT accessors.

@jkurdek jkurdek added the area-SwiftBindings Swift bindings for .NET label Dec 6, 2024
@jkurdek jkurdek self-assigned this Dec 6, 2024
@jkurdek
Copy link
Member Author

jkurdek commented Dec 6, 2024

\cc @stephen-hawley, @kotlarmilos, @MichalStrehovsky, @jkoritzinsky

@kotlarmilos
Copy link
Member

Primitive types are boxed in Swift: https://developer.apple.com/documentation/swift/int. A similar approach is used for nint in .NET: https://learn.microsoft.com/en-us/dotnet/api/system.nint?view=xamarin-ios-sdk-12. Marshaling primitive types into structs simplifies UX, which is good.

A lookup dictionary can be useful as a cache for frequently used types.

Can we combine these two approaches -- boxing for functionality and lookups for performance?

@jkurdek
Copy link
Member Author

jkurdek commented Dec 11, 2024

I think the question is whether we want to allow calling unconstrained generic methods with raw c# primitives, or the user should box the value before. (The user will have to do explicit boxing themselves if the generic parameter has constraints anyway)

@kotlarmilos
Copy link
Member

The user will have to do explicit boxing themselves if the generic parameter has constraints anyway

I believe we can rely on this. We need to understand how extensively it is used before committing to implementation. That said, the proposed approach looks good to me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-SwiftBindings Swift bindings for .NET
Projects
None yet
Development

No branches or pull requests

2 participants