-
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
Type nativeptr<unit> isn't usable at all #4166
Comments
Sorry I posted the wrong conversion code 😄 I see some problems with allowing unit to be an unmanaged type in general. I would suggest to add a function to FSharp.Core which allows a conversion from any nativeptr kind to |
Already created a Hacky fix for this in the compiler. It just changes in the type checker that the unit type passes the unmanaged constrain and the initial sample with stackalloc using unit works without runtime error. I assume that size of the type should be calculated as 1 byte? |
What are you trying to do? Casting to unit doesn’t really make any sense. |
@saul the constructor requires an void pointer :) I'm going to correct the example later, having Christmas right now. |
@saul I updated the example to be correct. |
Signed-off-by: realvictorprm <[email protected]>
Note that a workaround is to use IntPtr.ToPointer: let someOperationWithSpan () =
let data = NativePtr.stackalloc<byte>(256)
let voidPtr = (data |> NativePtr.toNativeInt).ToPointer()
let stackMemory = new Span<byte>(voidPtr, 256)
// Do some stuff with the span!
() Now the claimed inferred type of voidPtr is There is also IntPtr.op_Explicit for IntPtr -> void*, but there is no way of selecting the correct overload in F#. |
@poizan42 after I applied a fix in the compiler I ran into the same issue. This then is a bug I guess? |
@realvictorprm, yeah - there are actually several problems in F# like this happening because the same F# type can mean different .NET types. I think there might be more to fixing this than just marking unit as unmanaged, because unit can refer to both Microsoft.FSharp.Core.Unit and System.Void. |
Must be a bug. |
I am afraid it may need deeper changes in the system, than a few lines of change in the code. Without supporting the core low level stuff eg.: Reference semantics with value types constructs (all of them) F# may not be able to use the new constructs in the base libraries. The current unit type aliasing and the byref constructs are far from perfect in F#. I had to move for sevaral performance critical code to C# , because F# cannot express it properly for example structs types with indexed O(1) self element access.: "Another related language feature is the ability to declare a value type that must be stack allocated. In other words, these types can never be created on the heap as a member of another class. The primary motivation for this feature was Span and related structures. Span may contain a managed pointer as one of its members, the other being the length of the span. It's actually implemented a bit differently because C# doesn't support pointers to managed memory outside of an unsafe context. Any write that changes the pointer and the length is not atomic. That means a Span would be subject to out of range errors or other type safety violations were it not constrained to a single stack frame. In addition, putting a managed pointer on the GC heap typically crashes at JIT time." https://docs.microsoft.com/en-us/dotnet/csharp/reference-semantics-with-value-types |
Hm, I don't like this. @zpodlovics did you ever open a language suggestion etc. ? |
@realvictorprm Thanks for your help! I don't like it either. I would like to keep all the development in F# as much as possible, because I could retarget the high level parts later to different runtime eg.: fable, fez, fshlvm, etc. I ran into some corner cases and issues in the past with F# that was related with these low level stuff (unmanaged atomics, byref restrictions, struct units of measure types, struct du, , etc). Most of the issues was fixed, but some of them was remained unaddressed (which is not unreasonable, as the netstandard and sdk integration was lot more important). Please do not take this as offense, I would like to use Span as much as you, in fact I already tried to use it in development (with some C# helpers, because I am not yet figured out how to do it fully or partially in F# or inline IL eg.: by loading load a void* type to F# with custom typeofvoidptr function - something like tobyref for loading void* types #409). However reading the original design span design docs and ref like design docs (the .net core design reviews are also avaialble for span at https://www.youtube.com/playlist?list=PLRAdsfhKI4OWIV9TuZ0QmmlQzz41o80j0) I realized the missing parts are may larger than I earlier estimated (eg.: stack only types, readonly byref types, byref restrictions), and using it properly - especially in production - may require lot more. In production. I had to satisfy different constraints, and running into weird unsolvable corner cases is not fun. I would love to make a technically sound language change plans, but I am afraid I am not there yet. Here is the decompiled C# helper. Unfortunately I was not able to call the op_explicit cast without expressing the void* type somehow.
Span design docs: https://github.com/dotnet/corefxlab/blob/master/docs/specs/span.md |
@poizan42 I noticed that my fix also applies for the overload resolution. Now I try to find some examples for testing whether the feature works as expected. |
The next issue I ran into with Span is the Index access. It has the return type |
@realvictorprm Is this a subset of #4288? |
Well this is the issue tracking specifically that a void pts isn't usable
in F# but fixing the referenced issue would require this issue to be fixed
aswell.
Am 14.02.2018 5:30 vorm. schrieb "Phillip Carter" <[email protected]
…:
@realvictorprm <https://github.com/realvictorprm> Is this a subset of
#4288 <#4288>?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#4166 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AYPM8IOw2lZ-ikVWVCoRoWu9DEPv9xa-ks5tUmF-gaJpZM4RL8A->
.
|
It seems that the same code (with the .ToPointer() trick) no longer works with the .NET 2.1 Preview1 using open System
open Microsoft.FSharp.NativeInterop
[<EntryPoint>]
let main argv =
let data = NativePtr.stackalloc<byte>(256)
let voidPtr = (data |> NativePtr.toNativeInt).ToPointer()
let stackMemory = new Span<byte>(voidPtr, 256)
printfn "Hello World from F#!"
0 // return an integer exit code
Environment: Ubuntu 16.04 x86_64
Related issue: |
I don't know where that error is coming from, it's not an F# error |
Oh, it's a deprecation error....
|
@zpodlovics @dsyme All See the proposal and this constant in Roslyn. |
OK, I can see that raises the priority of Span work... |
@zpodlovics language suggestion at fsharp/fslang-suggestions#648 |
Note that while escape rules for ref structs in C# are rather complex, most of that comes from supporting wrapping C# supports things like: Method()
{
// obviously this span or its slices cannot be allowed to be returned from the containing method
Span<int> sp = stackalloc int[10];
} This is a fairly advanced scenario. In reality most of the ref-like structs are either transient to your code or wrap heap data (strings, arrays, etc..). In those cases escaping is not a concern. I'd recommend to not care about We had most of these rules already implemented for old "unboxable" types such as |
And, yes, Good explanation is here: Those features are useful on their own. Conceptually they just allow to specify that a byref is readonly. That could be used, for example, for passing around direct references to a large struct field or an array element just for reading while avoiding unnecessary copying. |
@VSadov We already have similar code snippets in F# due the latency / GC / performance requirements, but with NativePtr.stackalloc / NativePtr.read / NativePtr.write / NativePtr.get / NativePtr.set. [1] [2] [3] Please do NOT drop stackalloc support in F# and especially for Span just because it would be easier to implement initially (but at the same time the some thing stackalloc/Span will be possible in C#), because doing latency / gc / performance critical code would be imppossible in F# otherwise. The F# language will become medocrie tool for latency / gc / performance critical code compared to C#. F# should never be a second class citizen in .NET or other runtime for latency / gc / performance critical code. I intentionally used the Easy instead of Simple here. Using stackalloc and Span should be Simple in F#, but not necessary Easy to learn and do: https://github.com/matthiasn/talk-transcripts/blob/master/Hickey_Rich/SimpleMadeEasy.md |
Fun fact, it's not yet not possible (or I found a way) to cast a I tried the Unsafe package because it's not yet possible to express a byref<'T> -> nativeptr<'T> cast in F# when used it within a method (eg.: this.PassByRef<'T when 'T: unmanaged>(r: 'T byref)). AddressOf operator fails with "The byref type calue cannot be used at this point" the fixed operator fails with "Invaid use of fixed" because it's limited to address of a field address of an array element or string. The following example will not compile, because it will result a compilation error:
nativeptrunitunmanaged.fs module Test
open System
open System.Runtime.InteropServices
open System.Runtime.CompilerServices
open Microsoft.FSharp.NativeInterop
#nowarn "9"
[<Struct>]
type S =
val X: int
new(x)={X=x}
let toPtr (x: 'T byref) =
Unsafe.AsPointer<'T>(&x)
[<EntryPoint>]
let main argv =
let mutable s = S(1)
let vPtr = toPtr &s
let nPtr = vPtr |> NativePtr.toNativeInt
printfn "no nativeptr<unit> casting in F#"
0 // return an integer exit code
nativeptrunitunmanaged.fsproj:
using System;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
namespace S
{
struct S
{
public int V0;
public S(int v0) {
V0=v0;
}
}
class MainClass
{
public unsafe static void* AsPointer<T>(ref T value)
{
return Unsafe.AsPointer(ref value);
}
unsafe public static void Main (string[] args)
{
S s = new S(1);
void* vPtr = AsPointer(ref s);
int* iPtr = (int*)vPtr;
Console.WriteLine ("Hello World!");
}
}
} |
@zpodlovics i dont want to muddle up your issues with irrelevant stuff, but I was wondering if the following escape hatch works for your situation:
|
@abk-x Nope, already tried, also with type aliases, but you still cannot load the void* type. However thanks to @dsyme excellent work this will soon solved as voidptr type [1] and the related functions NativePtr in module. [1] https://github.com/Microsoft/visualfsharp/pull/4888/files#diff-085a94dd8e50b6ab66ae9a3263edcdabR70 |
Was going to suggest a pinvoke escape hatch, which I believe would work, but I think a language level thing is much more appropriate, and testable ! |
The proposal is to address this by adding Please take a look and review carefully, providing evil test cases where possible. |
This code is now possible with F# 4.5: open Microsoft.FSharp.NativeInterop
let someOperationWithSpan () =
let data = NativePtr.stackalloc<byte>(256)
let voidPtr = data |> NativePtr.toVoidPtr
let stackMemory = new Span<byte>(voidPtr, 256)
// Do some stuff with the span!
() Closing, since |
Framework code which uses the type
nativeptr<unit>
isn't usable in F# because we cannot cast any nativeptr tonativeptr<unit>
. Exact error is that unit isn't an unmanaged type.I noticed this after trying to compile simple examples with the Span type from the System.Memory package. Because the C# equivalent of
nativeptr<unit>
isvoid*
and nothing is wrong with a void pointer I thought reporting this here would be correct.To have a senseful usage example look at this code (requires the System.Memory package!):
The text was updated successfully, but these errors were encountered: