-
Notifications
You must be signed in to change notification settings - Fork 802
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
[RFCs FS-1051, FS-1052, FS-1053] support for span, readonly refs, byref-like structs #4888
Conversation
Right now proto is broken, am fixing that Once this starts to work I'd greatly appreciate help in testing/proofing it. It's not a feature I currently need so I'm likely to miss things. We missed at least one thing with ref returns (mentioned above - we should just fix it since ref returns are currently very rare) and that was because it was not proofed enough |
cc @drvink and @zpodlovics who should have some use cases to throw at this |
@cartermp I think it's a matter of systematically translating C# tests and samples (as I did above). |
@dsyme @cartermp "Future of C#" video from Build 2018 has a section about C# refs: https://youtu.be/QZ0rWLaMZeI?t=1214 using
|
@smoothdeveloper Thanks, yes, I've added those to the list of TODO items. Some of it is already done as part of F# 4.1 ref returns but there are more details to do. This series https://blogs.msdn.microsoft.com/mazhou/tag/c/ is a good guide |
Comment about local ref reassignment was moved to here: |
Ok, so this should compile according to the C# examples: type SpanLikeType = Span<int>
let m1 (x: byref<Span<int>>) (y: Span<byte>) =
// this is all valid, unconcerned with stack-referring stuff
let local = SpanLikeType()
x <- local
x I can't do a type alias to Span, but I don't know if that makes sense to do. |
Ok, here are the samples from C#: type SpanLikeType = Span<int>
let m1 (x: byref<Span<int>>) (y: Span<byte>) =
// this is all valid, unconcerned with stack-referring stuff
let local = SpanLikeType()
x <- local
x
let test1 (param1: byref<Span<int>>) (param2: Span<byte>) =
let stackReferring1 = Span<byte>()
let mutable stackReferring2 = Span<int>()
// this is allowed
stackReferring2 <- m1 &stackReferring2 stackReferring1
// this is NOT allowed
stackReferring2 <- m1 ¶m1 stackReferring1
// this is NOT allowed
param1 <- m1 &stackReferring2 stackReferring1
// this is NOT allowed
let param2 = stackReferring1.Slice(10)
// this is allowed
param1 <- Span<int>()
// this is allowed
stackReferring2 <- param1
let m2 (x: byref<Span<int>>) =
// should compile
&x
let test2 (param1: byref<Span<int>>) (param2: Span<byte>) =
let stackReferring1 = Span<byte>()
let mutable stackReferring2 = Span<int>()
let stackReferring3 = &(m2 &stackReferring2)
// this is allowed
stackReferring3 <- m1 &stackReferring2 stackReferring1
// this is allowed
(m2 &stackReferring3) <- stackReferring2
// this is NOT allowed
(m1 ¶m1) <- stackReferring2
// this is NOT allowed
param1 <- stackReferring3
// this is NOT allowed
&stackReferring3
// this is allowed
//¶m1 - uncomment to test return Currently, we violate some of these. Hopefully I did the conversion correctly. Here are the C# examples https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/span-safety.md#examples |
I want to mention these rely on stack information, so this code: let stackReferring1 = Span<byte>()
let mutable stackReferring2 = Span<int>() Isn't accurate, we need to find something analogous to this: Span<byte> stackReferring1 = stackalloc byte[10];
var stackReferring2 = new SpanLikeType(stackReferring1); |
OK, @TIHan and I have been over all remaining known cases and fixes and tests are now in for all of them. |
test Windows_NT Release_ci_part2 Build please |
More testing today, looking at This code: [<Struct>]
type TestMut =
val mutable x : int
member this.AnAction() =
this.x <- 1
let testIn (m: inref<TestMut>) =
m.x <- 1
let testAction (m: inref<TestMut>) =
m.AnAction()
printfn "%A" m.x
let test() =
let x = TestMut()
testIn (&x)
testAction (&x)
x Should not compile due to -- I also want to make note of some IL generated when calling a method that takes a ref, it's not super important or a big concern because the JIT optimizes this very well (identical if I were to do the C# equivelant). Here is the example: [<Struct>]
type TestMut =
val mutable x : int
member this.AnAction() =
this.x <- 1
[<MethodImpl(MethodImplOptions.NoInlining)>]
let testIn (m: inref<TestMut>) =
Console.WriteLine(m.x)
[<MethodImpl(MethodImplOptions.NoInlining)>]
let testRef (m: byref<TestMut>) =
Console.WriteLine(m.x)
let callTestIn() =
let x = TestMut()
testIn (&x)
let callTestInMutable() =
let mutable x = TestMut()
testIn (&x)
let callTestRefMutable() =
let mutable x = TestMut()
testRef (&x) And here is the IL generated for
It looks like we have too many locals init here where as a C# equivalent code would look like this:
But, looking at the JIT ASM for either, they generate the same ASM code. So it probably does not matter, just wanted to make note of it. |
Currently, tests are failing on the let test() =
let addr = &f (C())
addr <- addr + 1
let addr = &f (ObjExpr())
addr <- addr + 1
check2 "cepojcwem16" 3 x It's failing on this line |
Another minor issue: [<Struct>]
type TestMut =
val mutable x : int
static member Test(x: inref<TestMut>) = ()
static member CallTest() =
TestMut.Test(TestMut())
let testIn (m: inref<TestMut>) =
()
let callTestIn() =
testIn (TestMut()) This doesn't compile due to this line: |
This is another minor issue: [<Struct>]
type TestS =
static member Test(x: outref<TestS>) = ()
let testIn (m: outref<TestS>) =
() This should not compile and give you an error saying that your Since the checks haven't been there before anyway, I don't think we have to include them in 4.5 - we could do them later if it requires a lot more work. |
Thanks. All fixes are in and being validated in CI.
Yes, it is by design. I've noted it in the RFC. I understand why you would expect the same conversions to take place, but we currently don't do any such conversions (e.g. F# lambda --> System.Func) on calls to let-bound functions, and I'm loathe to start doing them here , it would be better to make a systematic change here that took into account all conversions.
Yes, we don't make this check today and it won't be part of F# 4.5. I've noted that in the RFC
Yes, it's ok, we generate some more locals but the JIT does a good job of them |
test Ubuntu16.04 Release_default Build please |
@dotnet-bot test Ubuntu16.04 Release_default Build please |
And we got it! Any bugs we find, we will thoroughly review with the C# team; but so far, I feel this is ready considering the massive testing we have done, especially for a personal project that uses this stuff heavily. |
* Update README.md * Update README.md * Update README.md * [RFCs FS-1051, FS-1052, FS-1053] support for span, readonly refs, byref-like structs (#4888) * initial support for span, readonly refs, byref-like structs * fix proto build * make proto work with previous FSharp.Core * make proto work with previous FSharp.Core * update baselines * integrate code cleanup * integrate code cleanup * integrate code cleanup * integrate code cleanup * fix build * fix build * implicit deref of byref returns * add tests for Memory, ReadOnlySpan and ReadOnlyMemory * fix tests * simplify diff * simplify diff * remove duplicate error messages * fix build * test updates * fix build * fix build * update baselines * fix uses of NativePtr.toByRef * switch to inference using byref pointer capabilities * fix proto build * update baselines, byref extension methods * fix test errors * emit in,out,modreq attributes correctly * update tests * fix build * fix build * fix tests * fix tests * get it right silly boy * fix test * minor cleanup * add more tests * clarify overloading behaviour + test case * fix build break * fix build of tests * update tests * add more tests * byref fixes * updates for subsumption calls, error message, assign-to-return-byref * test updates, implicit deref on byref return for normal functions * update baseline * improve debug formatting, better error message on implicit deref, improve error messages * add more tests for recursive functions * update baselines * fix baselines * updates for new test cases * updates for new test cases * test updates and byref-to-byreflike * deal with 'M() <- expr' * restrict addresses of immutable top-level things * fix IsByRefLike on struct * update tests * fix test * fix test * improve check for no-return-of-struct-field-addresses * fix test case * Provide fast generic comparer for bool values (#5076) * provide fast generic comparer for bool values * formatting * no completion on name of value and function declaration (#5083) * LOC CHECKIN | Microsoft/visualfsharp master | 20180604 | Termchange (#5082) * fix merge
…field is which in dotnet#4888 removing the comment and naming the fields
…field is which in dotnet#4888 removing the comment and naming the fields
…field is which in dotnet#4888 (dotnet#8139) removing the comment and naming the fields
Adding support for some C# 7.0-7.2 features:
RFCs
Simple samples compile, e.g. taken from here
TODO
ReadOnlyMemory
andReadOnlySpan
&
is used. currently you must assign to a local and then use that local (where the use of the local does an automatic dereference)modreq
oninref
parameters