组合内联与显式成员约束时的奇怪错误

Abe*_*bel 6 f# compiler-errors type-parameter

(更新:我添加了一个repro示例)

代码如下所示:

type Lib =
    static member inline tryMe (a: ^a) = 
        let name = (^a: (static member name: string) ())
        name

type Test =
    struct 
        val Value: string
        new v = {Value = v}
    end
    static member inline name with get() = "HiThere"
    static member works(a:Test) = Lib.tryMe a
Run Code Online (Sandbox Code Playgroud)

这将"正常工作"并编译.但是,如果你稍微扩展它,例如如下所示:

/// Does a bounds check and raises an error if bounds check is not met
let inline checkBounds f (g: 'b -> ^c) (tp: ^a) = 
    let convertFrom = (^a: (static member name: string) ())
    let convertTo = (^c: (static member name : string) ())
    let value =  (^a: (member Value: 'b) tp)
    if f value then
        g value
    else 
        failwithf "Cannot convert from %s to %s." convertFrom convertTo


type ConverterA =
    struct 
        val Value: sbyte
        new v = { Value = v }
    end

    static member inline name with get() = "converter-a"
    static member inline convert (x: ConverterA) : ConverterB = 
        checkBounds ((>=) 0y) (byte >> ConverterB) x

and ConverterB =
    struct 
        val Value: byte
        new v = { Value = v }
    end
    static member inline name with get() = "converter-b"
Run Code Online (Sandbox Code Playgroud)

它会引发一大堆虚假的FSharp编译器错误.

错误FS1114:值'Foo.Bar.name'被内联标记但未在优化环境中绑定

错误FS1113:值'name'被标记为内联,但其实现使用了一个内部或私有函数,该函数无法访问

警告FS1116:标记为"内联"的值具有意外值

错误FS1118:无法内联标记为"内联"的值"name",可能是因为递归值标记为"inline"

我还没有看到其他内联函数发生这种情况.我不确定这里发生了什么.如果我稍微改变一下,例如删除该convertTo行及其依赖项,它编译得很好.

在FSI中运行代码时,即使设置了FSI,也不会出现错误--optimize.

我可以通过删除解决它inline.对于这种领域而言,无论如何,JIT都会内联它们,即使F#没有.

这是编译器错误吗?或者我的代码中是否有错误,或者对我没有意识到的显式成员约束有一些限制?

Cod*_*key 1

您需要重新排序,以便您使用的函数在使用时就已知,否则 F# 编译器似乎不知道要内联什么。正如您在下面的评论中所说,如果您问我,这个答案就是一个错误。

/// Does a bounds check and raises an error if bounds check is not met
let inline checkBounds f (g: 'b -> ^c) (tp: ^a) = 
    let convertFrom = (^a: (static member name: string) ())
    let convertTo = (^c: (static member name : string) ())
    let value =  (^a: (member Value: 'b) tp)
    if f value then
        g value
    else 
        failwithf "Cannot convert from %s to %s." convertFrom convertTo

type ConverterB =
    struct 
        val Value: byte
        new v = { Value = v }
    end
    static member inline name with get() = "converter-b"

and ConverterA =
    struct 
        val Value: sbyte
        new v = { Value = v }
    end

    static member inline name with get() = "converter-a"
    static member inline convert (x: ConverterA) : ConverterB = 
        checkBounds ((>=) 0y) (byte >> ConverterB) x
Run Code Online (Sandbox Code Playgroud)