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

Support ref structs in generics #16800

Closed
vzarytovskii opened this issue Mar 4, 2024 · 9 comments · Fixed by #17597
Closed

Support ref structs in generics #16800

vzarytovskii opened this issue Mar 4, 2024 · 9 comments · Fixed by #17597
Assignees
Labels
Area-Compiler-Checking Type checking, attributes and all aspects of logic checking Area-Compiler-ImportAndInterop import of .NET DLLs and interop Feature Improvement Feature Request Needs-RFC
Milestone

Comments

@vzarytovskii
Copy link
Member

vzarytovskii commented Mar 4, 2024

.NET 9 will support ref structs in generics as well as new constraint.
A bunch of new APIs will be utilising it, hence it needs to be supported in F#

Runtime support: dotnet/runtime#65112
C#: https://github.com/dotnet/csharplang/blob/main/proposals/ref-struct-interfaces.md

Note: only consuming is in scope, everything else isn't possible right now, due to lack of many ref-related features support in compiler (ref fields, escape analysis as examples).

@brianrourkeboll
Copy link
Contributor

Note: only consuming is in scope, everything else isn't possible right now, due to lack of many ref-related features support in compiler (ref fields, escape analysis as examples).

It's probably worth keeping in mind this feature's potential usefulness in someday addressing things like fsharp/fslang-suggestions#688, fsharp/fslang-suggestions#805, fsharp/fslang-suggestions#887, fsharp/fslang-suggestions#1140, #7828.

@vzarytovskii
Copy link
Member Author

Note: only consuming is in scope, everything else isn't possible right now, due to lack of many ref-related features support in compiler (ref fields, escape analysis as examples).

It's probably worth keeping in mind this feature's potential usefulness in someday addressing things like fsharp/fslang-suggestions#688, fsharp/fslang-suggestions#805, fsharp/fslang-suggestions#887, fsharp/fslang-suggestions#1140, #7828.

Yes, however this feature only covers consuming such constraints, and give possibility to interact with such APIs, everything else is separate and will require additional work around byrefs and byref-likes in compiler (around escape analysis, supporting ref fields, scoped refs, unscoped refs, etc).

@brianrourkeboll
Copy link
Contributor

Yep, I understand.

I just meant that we should try to ensure that whatever approach is taken to add support for consumption doesn't limit our options if we also someday wanted to support production, or that thinking about the latter might have the potential to inform some aspects of how the former is done.

@vzarytovskii vzarytovskii added Area-Compiler-ImportAndInterop import of .NET DLLs and interop and removed Needs-Triage labels Mar 4, 2024
@brianrourkeboll
Copy link
Contributor

brianrourkeboll commented Mar 4, 2024

(Let me know if you'd rather I delete this here and wait to comment on the RFC.)

...The reason I linked those specific issues in #16800 (comment) is that they present concrete scenarios where using this feature productively might be useful.

For example, do we plan to surface the new constraint in the type signature when a construct using the constraint defined in C# is consumed by F# code?

Even if we don't plan to allow producing such a constraint in F# for now, thinking about what that might look like might inform how we represent such a signature, e.g., consider:

let inline (|>) x (f : 'a -> 'b when :? byref<'a> and 'a :? struct) = f x
type SomeBuilder () =
    member inline _.Bind (x, [<InlineIfLambda>] f : 'a -> M<'b> when :? byref<'a> and 'a :? struct) =

— which would be analogous to the C# —

public static B op_PipeRight<A, B>(A x, Func<A, B> f) where A : allows ref struct => f(x);
public class SomeBuilder
{
    public M<B> Bind<A, B>(M<A> x, Func<A, M<B>> f) where A : allows ref struct {}
}

— etc.

Using :? for "might be" immediately strikes me as one potential way to naturally represent allows in F#, by the same logic that we already use : for "is" and :> for "is convertible to" in both the grammar of types and the grammar of expressions.

@vzarytovskii
Copy link
Member Author

We probably want it at some point, but currently the most important thing is to support consuming them.

I think we should create a discussion of what are we missing from the byrefs features (both importing, checking and codegen), have a chat there, with examples and use cases from C#, and from there we should create a new suggestion for improvements (I do believe it should be one rfc, but can be multiple contributions).

@amongonz
Copy link

amongonz commented Apr 1, 2024

Even if F# 9 only supported consumption, please consider whether the new anti-constraint could already be added to intrinsics such as raise, defaultof, sizeof and typeof (and eventually, in this or a future release, to the raise helpers).

These intrinsics fill the need for equivalent keywords in F# and they're inherently safe to use with byref-like types, but by acting like generic functions they became incompatible with byref-likes. At the very least, add it to raise: multiple times I've had to write raise (...); ByRefLikeType() (dead code for a default constructor), which may not be an obvious workaround.

@vzarytovskii
Copy link
Member Author

Even if F# 9 only supported consumption, please consider whether the new anti-constraint could already be added to intrinsics such as raise, defaultof, sizeof and typeof (and eventually, in this or a future release, to the raise helpers).

These intrinsics fill the need for equivalent keywords in F# and they're inherently safe to use with byref-like types, but by acting like generic functions they became incompatible with byref-likes. At the very least, add it to raise: multiple times I've had to write raise (...); ByRefLikeType() (dead code for a default constructor), which may not be an obvious workaround.

Yeah, probably adjusting some fslib functions will be a good idea.

@brianrourkeboll
Copy link
Contributor

On the topic of supporting consumption and how that might look in F#:

I thought about this a bit more the other week — see here dotnet/csharplang#7608 (comment) for more detail — and I believe that one way to think of the addition of this "anti-constraint" to a generic type parameter is as a unioning of a constraint like 'T : byref<struct> with the existing default constraint union implied by bare 'T, i.e., 'T when 'T : struct or 'T : not struct.1

C#'s where T : allows ref struct could thus be thought of (in imaginary F# syntax) as when 'T : struct or 'T : not struct or 'T : byref<struct>.

I'm not suggesting that we start making the existing implicit default constraint union explicit, or that we necessarily add support for constraint unions as a general feature.

I am suggesting that the idea of constraint unions may help us approach the ref struct "anti-constraint" feature in a more general way than if we think of it as a one-off special case. I'm also open to the possibility that it's not helpful, but I figured I'd bring it up.

Footnotes

  1. Using or for constraint unions would align with the existing use of and for constraint intersections.

@AaronRobinsonMSFT
Copy link
Member

Support has been added to all officially supported runtimes - CoreCLR, native AOT and mono. I believe this has made it into the latest .NET 9 Preview. There are some tricky bits to be aware as it relates to special IL sequences that are recognized by the JIT and mono - see here. IL based tests can be found at here.

/cc @jaredpar @lambdageek @fanyang-mono

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Compiler-Checking Type checking, attributes and all aspects of logic checking Area-Compiler-ImportAndInterop import of .NET DLLs and interop Feature Improvement Feature Request Needs-RFC
Projects
Archived in project
Development

Successfully merging a pull request may close this issue.

5 participants