Why does the F# compiler give an error for one case but not the other?

安稳与你 提交于 2019-12-12 11:04:10

问题


I'm working on a platform invoke call from F#, and I am getting a compiler error I really can't make that much sense out of. First, let me show the C signature of what I am doing:

int Foo(
    ULONG_PTR *phHandle,
    DWORD flags
);

In F#, I think the correct way to invoke this natively is as so:

[<DllImport("somedll.dll")>]
static extern int APlatformInvokeCall
    (
        [<Out>]nativeint& phHandle,
        uint32 flags
    )

If I try to call this in a class, I get a compilation error when calling it like so:

type Class1() = 
    [<DllImport("somedll.dll")>]
    static extern int APlatformInvokeCall
        (
            nativeint& phHandle,
            uint32 flags
        )

    member this.Foo() =
        let mutable thing = nativeint 0
        APlatformInvokeCall(&thing, 0u) |> ignore
        thing

The error is:

A type instantiation involves a byref type. This is not permitted by the rules of Common IL.

Weirdly, when I do this all in a module, the compilation errors go away:

module Module1 = 
    [<DllImport("somedll.dll")>]
    extern int APlatformInvokeCall
        (
            nativeint& phHandle,
            uint32 flags
        )

    let Foo() =
        let mutable thing = nativeint 0
        APlatformInvokeCall(&thing, 0u) |> ignore
        thing

Why does this compile as a module, but not as a class?


回答1:


I don't think it's valid to define an extern method within a class in F#.

If you pull up the F# 3.0 language specification and search for DllImport, near the bottom is a table listing some special attributes and how they can be used. The text for [<DllImport>] says:

When applied to a function definition in a module, causes the F# compiler to ignore the implementation of the definition, and instead compile it as a CLI P/Invoke stub declaration.

That seems to indicate that it's only valid to declare extern methods (that use [<DllImport>]) on functions defined in a module; it doesn't say anything about class members though.

I think you're running into a compiler bug. Please submit this code to fsbugs@microsoft.com so they can fix the error message emitted by the compiler -- it should really be giving you an error about defining an extern method in a class since that's not allowed by the language spec.




回答2:


Whether this is a bug not withstanding, maybe this is what's going on: If APlatformInvokeCall were considered a static member function, that member have a single argument of tuple type. Tuples are compiled into objects of generic type (see here, at the bottom, or 5.1.3 in the spec). In this case that tuple is

System.Tuple<nativeint&, uint32>

But ECMA 335 II.9.4 says you can't instantiate generic types at byref types. This explains the error reported.

This explanation fits the fact mentioned above that Class1 works (well, compiles) if you modify the extern declaration and call to take instead a single argument. It also fits the fact that the module version works, since in that version there is no considering APlatFormInvokeCall a member function.




回答3:


The simple solution is to check the spec, here is the class definition grammar:

type type-name pat_opt as-defn)opt =
    class
        class-inherits-decl_opt
        class-function-or-value-defns_opt
        type-defn-elements
    end

then we have

class-function-or-value-defn :
      attributes_opt staticopt let rec_opt function-or-value-defns
      attributes_opt staticopt do expr

which doesn't allow extern.

and

type-defn-element :
      member-defn
      interface-impl
      interface-signature

which isn't what you want either.

As a result, we can see that using extern as you are trying to use it can't be done inside a class.



来源:https://stackoverflow.com/questions/22275072/why-does-the-f-compiler-give-an-error-for-one-case-but-not-the-other

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!