Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compiler bug in F# 4?

Tags:

f#

I have some F# 4.0 source that compiles fine in Debug, but not in Release.

There are no conditional defines, no changes in the inferred types, and nothing else I can think of, that can explain this difference to me.

Did I really stumble on a compiler bug?

This is a snippet with the problematic code.

let oldItems = userDisplayItems |> Seq.toList
for newItem in newItems do
    match List.tryFind (fun (itemOfOld: UserDisplay.UserDisplayItem) -> itemOfOld.Id = newItem.Id) oldItems with
    | Some oldItem ->

The error message refers to the last use of "oldItems", before the "with" keyword at the end of the long line. The error message is:

Undefined value 'oldItems : UserDisplayItem list'

What!? oldItems is in plain sight a few lines above, and this compiles in Debug, so why not in Release? What does that error message actually mean?

UserDisplayItem is a simple class. newItems is a ResizeArray of UserDisplayItem

I looked into the build history, and it compiled fine in Release when UserDisplayItem was an F# immutable record, and not a class.

Visual Studio 2015, F# 4.0, Any CPU, Release, .NET 4.5.2 targeted.

UPDATE:

The following is a complete example. You can create an F# console application, and paste this into Program.fs. I expect it will compile in Debug, but not Release.

open System.Collections.ObjectModel

type User = { Id: int }

[<AllowNullLiteral>]
type UserDisplayItem(id: int) =
    let mutable id = id
    member x.Id with get() = id and set(v) = id <- v

let userDisplayItems = new ObservableCollection<UserDisplayItem>()

let refreshList () =
    let newItems = userDisplayItems
    let oldItems = userDisplayItems |> Seq.toList
    for newItem in newItems do
        match List.tryFind (fun (itemOfOld: UserDisplayItem) -> itemOfOld.Id = newItem.Id) oldItems with
        | Some oldItem -> ()
        | None -> ()

UPDATE 2 :

An even shorter sample.

type UserDisplayItem = { Id: int }

let refreshList () =
    let newItems = new ResizeArray<UserDisplayItem>()
    let oldItems = new ResizeArray<UserDisplayItem>() |> Seq.toList
    for newItem in newItems do
        match List.tryFind (fun (itemOfOld: UserDisplayItem) -> itemOfOld.Id = newItem.Id) oldItems with
        | Some oldItem -> ()
        | None -> ()
like image 726
Bent Tranberg Avatar asked Jun 15 '16 08:06

Bent Tranberg


Video Answer


2 Answers

Seems to be compiler bug (maybe related to 1020).
Could reproduce it with your code and F# version 14.0.23413.0
Now installed current preview which is F# version 14.0.23618.0 and it works.

like image 197
DAXaholic Avatar answered Nov 03 '22 22:11

DAXaholic


Until you have the fix for this issue (see DAXaholic's answer for final fix), you can use this workaround, as explained by dsyme here:

https://github.com/Microsoft/visualfsharp/issues/759#issuecomment-162243299

The fix applied to the last sample in the question. First add this.

// do something that doesn't get optimized away
let workaround() = null |> ignore  

and then only add a call to the function workaround in the right place(s), which is right before any loop where you get this error.

let oldItems = new ResizeArray<UserDisplayItem>() |> Seq.toList
workaround()
for newItem in newItems do
    match List.tryFind (fun (itemOfOld: UserDisplayItem) -> itemOfOld.Id = newItem.Id) oldItems with
like image 33
Bent Tranberg Avatar answered Nov 03 '22 20:11

Bent Tranberg