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

[Swift language features] Add support for passing bound generics to swift functions #2970

Open
1 of 4 tasks
kotlarmilos opened this issue Jan 30, 2025 · 6 comments
Open
1 of 4 tasks
Assignees
Labels
area-SwiftBindings Swift bindings for .NET User Story A single user-facing feature. Can be grouped under an epic.

Comments

@kotlarmilos
Copy link
Member

kotlarmilos commented Jan 30, 2025

Description

We want to support the following:

public func sumArray(array: Array<Int32>) -> Int32
{
    return array.reduce(0, +)
}

public func getArray(a: Int32, b: Int32) -> Array<Int32>
{
    return [a, b]
}

Sub tasks:

@jkurdek
Copy link
Member

jkurdek commented Jan 30, 2025

Isn't it already working? For array I think you need to add the manual projection to type database.

<entity managedNameSpace="Swift.Runtime" managedTypeName="SwiftArray">
    <typedeclaration kind="struct" name="Array`1" module="Swift" mangledName="sSa" frozen="false" blittable ="false"/>
</entity>

@kotlarmilos
Copy link
Member Author

I tried to add it as a test, but the method emitter is missing marshalling of generic types:

public static Int32 sumArray( SwiftArray<Int32> array)
        {
            try
            {
                
                var result = PInvoke_sumArray(array);
                
                return result;
            }
            
            finally
            {
            }
            
        }
        
        [UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvSwift) })]
        [DllImport("./libHelloLibrary.dylib", EntryPoint = "$s12HelloLibrary8sumArray5arrays5Int32VSayAEG_tF")]
        private static extern Int32 PInvoke_sumArray( SwiftArray<Int32> array);
        
        
        public static SwiftArray<Int32> getArray( Int32 a,  Int32 b)
        {
            try
            {
                
                var result = PInvoke_getArray(a, b);
                
                return result;
            }
            
            finally
            {
            }
            
        }
        
        [UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvSwift) })]
        [DllImport("./libHelloLibrary.dylib", EntryPoint = "$s12HelloLibrary8getArray1a1bSays5Int32VGAF_AFtF")]
        private static extern SwiftArray<Int32> PInvoke_getArray( Int32 a,  Int32 b);

@jkurdek
Copy link
Member

jkurdek commented Jan 30, 2025

Ok, I understand. Is this necessary for milestone 1?

Passing bound (or closed) generic types is going to be a challenge.

@kotlarmilos
Copy link
Member Author

Not needed, we can wrap it for the M1.

@jkurdek jkurdek added the User Story A single user-facing feature. Can be grouped under an epic. label Feb 3, 2025
@jkurdek jkurdek changed the title [Swift language features] Allow passing generic types in methods [Swift language features] Add support for passing bound generics to swift functions Feb 3, 2025
@jkurdek
Copy link
Member

jkurdek commented Feb 3, 2025

This is a complex issue. To support passing bound generics into Swift functions, we need to correctly lower them when the generics and their parameters are frozen. So far, we have identified two approaches to address this problem:

Per-Instantiation Helper Structs (as described in binding-generics.md):
We can generate a helper struct for each instantiation of a generic. For example, if we have Foo<T>, we would generate structs like Foo_Int, Foo_Double, etc. This allows the projection to work with simple (non-generic) structs that the runtime can lower correctly.
The main challenge with this approach is the need to calculate the size, stride, and alignment of these helper structs. This requires maintaining full type layout information across different modules (e.g., creating a new bound generic in Module B for a type defined in Module A) and implementing Swift’s type layout algorithm at the projection tooling level.

Generic Helper Structs (proposed by @jkoritzinsky):
If we can accurately calculate the layout of a generic struct at runtime and handle the lowering correctly, we can use a single generic helper struct instead of generating non-generic ones for each instantiation. This approach simplifies the model from the first approach because we don’t need to maintain layout information across modules or compute size, stride, and alignment at the projection tooling level. This approach requires adding new features to dotnet/runtime.

@jkurdek
Copy link
Member

jkurdek commented Feb 3, 2025

Swift.Array is a frozen generic - it is a bit special in a sense that its layout is independent of the generic parameter. So it will always undergo lowering. Current Swift.Array implementation also gives us the appropriate helper Struct for free. We can use ArrayBuffer instead of Swift.Array for projections and just have a thin wrapper on C# side which wraps it in the correct class.

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 User Story A single user-facing feature. Can be grouped under an epic.
Projects
None yet
Development

No branches or pull requests

2 participants