尽管尾部呼叫位置仍然堆栈溢出但仅在64位

pri*_*tor 6 stack-overflow 64-bit f# 32-bit tail-recursion

源于这个问题,我有这个小F#代码(github)根据正态分布生成随机值:

// val nextSingle : (unit -> float32)
let nextSingle =
    let r = System.Random()
    r.NextDouble >> float32

// val gauss : (float32 -> float32 -> seq<float32>)
let gauss mean stdDev =
    let rec gauss ready = seq {
        match ready with
        | Some spare ->
            yield spare * stdDev + mean
            yield! gauss None
        | _ ->
            let rec loop () =
                let u = nextSingle() * 2.f - 1.f
                let v = nextSingle() * 2.f - 1.f
                let s = pown u 2 + pown v 2
                if s >= 1.f || s = 0.f then loop() else
                u, v, s
            let u, v, s = loop()
            let mul = (*)(sqrt(-2.f * log s / s))
            yield mul u * stdDev + mean
            yield! mul v |> Some |> gauss
    }
    gauss None
Run Code Online (Sandbox Code Playgroud)

对我而言,这似乎只能在尾部呼叫位置调用自己,而且在StackOverflowException启用TCO时不会导致错误.但它确实运行时,64位.它运行时,32位(即"首选32位"复选框,在项目设置).

我正在使用.NET Framework 4.5.2和F#4.4.0.0.

有人可以解释导致问题的原因吗?

kvb*_*kvb 8

看起来像编译器的序列表达式编译机制中的错误.这是一个简化的复制品:

let rec loop r = seq {
    if r > 0 then
        let rec unused() = unused()
        yield r
        yield! loop r
}

printfn "%i" (Seq.nth 10000000 (loop 1))
Run Code Online (Sandbox Code Playgroud)

显然,未使用的递归定义的存在不应该影响它是否会产生堆栈溢出,但确实如此.